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 428d55b2fb..b60171cf2d 100644 --- a/.gitignore +++ b/.gitignore @@ -73,4 +73,10 @@ bin/ # PMD files .pmd .ruleset -test-output/ \ No newline at end of file +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/.travis.yml b/.travis.yml deleted file mode 100644 index 6e595b4e93..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: java -jdk: -- openjdk8 - -# prevent travis running gradle assemble; let the build script do it anyway -install: true - -# running in container causes test failures and 2x-3x longer build, use standalone instances -sudo: required - -# script for build and release via Travis to Bintray -script: gradle/buildViaTravis.sh - -# Code coverage -after_success: - - bash <(curl -s --retry 10 https://codecov.io/bash) - - bash gradle/push_javadoc.sh - -# cache between builds -cache: - directories: - - $HOME/.m2 - - $HOME/.gradle -env: - global: - - secure: YcLpYfNc/dyDON+oDvnJK5pFNhpPeJHxlAHV8JBt42e51prAl6njqrg1Qlfdp0pvBiskTPQHUxbFy9DOB1Z+43lPj5vlqz6qBgtS3vtBnsrczr+5Xx7NTdVKq6oZGl45VjfNPT7zdM6GQ5ifdzOid6kJIFu34g9JZkCzOY3BWGM= - - secure: WVmfSeW1UMNdem7+X4cVDjkEkqdeNavYH4udn3bFN1IFaWdliWFp4FYVBVi+p1T/IgkRSqzoW9Bm43DABe1UMFoErFCbfd7B0Ofgb4NZAsxFgokHGVLCe6k5+rQyASseiO7k0itSj3Kq9TrDueKPhv+g+IG0w1A8yZTnXdhXHvY= - - secure: Xt8E09nmSr+5r7ly95hG/EiBitZbhFGPRGp8oqPkNn1A2fzG9+hnvlNLgQhVPsISZGzJwkWa3LGBxAVGmuysVOz7eCwkoqlDZaaSLYAPfWXqkr+cmYGPkErgHSp+n/hnQG4TylX0YxzqX8flr6db21zWyNduiyHmo+xFydI5LeM= - - secure: RmpIsmYa5BdLLWR6DILjhEE/dx2q3O0NIkvnMx5G1cyRCNCrOf1B7fYFHnsTDwpvRA+6H6dZinmeyf6D3G+czOG5q/TW2jcu5nh+YOLhBb6jPIqRDfq/WHAa5Lkdssxs5g9RdWlEDVFMoE62lGc4cnfJz5F5puH29dy2SvXxIQw= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4547f563a8..98ae7225f3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,19 +13,16 @@ 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 (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. */ ``` 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/README.md b/README.md index 7c120db700..5276c0bd37 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # RxJava: Reactive Extensions for the JVM - + [![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.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava) +[![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) 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. @@ -10,22 +12,27 @@ It extends the [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) #### Version 3.x ([Javadoc](http://reactivex.io/RxJava/3.x/javadoc/)) -- single dependency: [Reactive-Streams](https://github.com/reactive-streams/reactive-streams-jvm) -- continued support for Java 6+ & [Android](https://github.com/ReactiveX/RxAndroid) 2.3+ -- fixed API mistakes and many limits of RxJava 2 -- intended to be a replacement for RxJava 2 with relatively few binary incompatible changes -- Java 8 lambda-friendly API -- 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 +- 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. +: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) will be supported with bugfixes and important documentation updates until -**December 31, 2020**. No new features will be added to 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 @@ -41,7 +48,7 @@ The first step is to include RxJava 3 into your project, for example, as a Gradl implementation "io.reactivex.rxjava3:rxjava:3.x.y" ``` -(Please replace `x` and `y` with the latest version numbers: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.reactivex.rxjava3/rxjava) +(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 @@ -60,19 +67,6 @@ public class HelloWorld { } ``` -If your platform doesn't support Java 8 lambdas (yet), you have to create an inner class of `Consumer` manually: - -```java -import io.reactivex.rxjava3.functions.Consumer; - -Flowable.just("Hello world") - .subscribe(new Consumer() { - @Override public void accept(String s) { - System.out.println(s); - } - }); -``` - 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 @@ -207,7 +201,7 @@ RxJava operators don't work with `Thread`s or `ExecutorService`s directly but wi - `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 `JavaFXSchedulers.gui()`. +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). @@ -516,7 +510,7 @@ For further details, consult the [wiki](https://github.com/ReactiveX/RxJava/wiki - Google Group: [RxJava](http://groups.google.com/d/forum/rxjava) - Twitter: [@RxJava](http://twitter.com/RxJava) - [GitHub Issues](https://github.com/ReactiveX/RxJava/issues) -- StackOverflow: [rx-java](http://stackoverflow.com/questions/tagged/rx-java) and [rx-java2](http://stackoverflow.com/questions/tagged/rx-java2) +- 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 @@ -537,7 +531,7 @@ APIs marked with the [`@Experimental`][experimental source link] annotation at t #### @Deprecated -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. +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. #### io.reactivex.rxjava3.internal.* @@ -557,7 +551,7 @@ Binaries and dependency information for Maven, Ivy, Gradle and others can be fou Example for Gradle: ```groovy -compile 'io.reactivex.rxjava3:rxjava:x.y.z' +implementation 'io.reactivex.rxjava3:rxjava:x.y.z' ``` and for Maven: @@ -575,18 +569,22 @@ and for Ivy: ``` -Snapshots are available via https://oss.jfrog.org/libs-snapshot/io/reactivex/rxjava3/rxjava/ +### Snapshots + +Snapshots after May 19st, 2025 are available via https://central.sonatype.com/repository/maven-snapshots/io/reactivex/rxjava3/rxjava/ ```groovy repositories { - maven { url 'https://oss.jfrog.org/libs-snapshot' } + maven { url 'https://central.sonatype.com/repository/maven-snapshots' } } dependencies { - compile 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' + implementation 'io.reactivex.rxjava3:rxjava:3.0.0-SNAPSHOT' } ``` +JavaDoc snapshots are available at https://reactivex.io/RxJava/3.x/javadoc/snapshot + ## Build To build: @@ -620,5 +618,5 @@ For bugs, questions and discussions please use the [Github Issues](https://githu 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/annotations/Beta.java -[experimental source link]: https://github.com/ReactiveX/RxJava/blob/3.x/src/main/java/io/reactivex/annotations/Experimental.java +[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 aaef86c824..8bcfddb717 100644 --- a/build.gradle +++ b/build.gradle @@ -1,80 +1,43 @@ -buildscript { - - // Dependency versions - // --------------------------------------- - - ext.reactiveStreamsVersion = "1.0.3" - ext.junitVersion = "4.12" - ext.testNgVersion = "7.0.0" - ext.mockitoVersion = "3.2.0" - ext.jmhLibVersion = "1.21" - ext.jmhGradleVersion = "0.5.0" - ext.guavaVersion = "28.1-jre" - ext.jacocoVersion = "0.8.4" - ext.animalSnifferVersion = "1.5.0" - ext.licenseVersion = "0.15.0" - ext.bintrayVersion = "1.8.4" - ext.jfrogExtractorVersion = "4.11.0" - ext.bndVersion = "4.3.1" - ext.checkstyleVersion = "6.19" - - // -------------------------------------- - - repositories { - jcenter() - mavenCentral() - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "ru.vyarus:gradle-animalsniffer-plugin:$animalSnifferVersion" - classpath "gradle.plugin.com.hierynomus.gradle.plugins:license-gradle-plugin:$licenseVersion" - classpath "me.champeau.gradle:jmh-gradle-plugin:$jmhGradleVersion" - classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$bintrayVersion" - classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$jfrogExtractorVersion" - classpath "biz.aQute.bnd:biz.aQute.bnd.gradle:$bndVersion" - } -} - -group = "io.reactivex.rxjava3" -ext.githubProjectName = "rxjava" - -version = project.properties["release.version"] - -def releaseTag = System.getenv("TRAVIS_TAG"); +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") +} + +ext { + reactiveStreamsVersion = "1.0.4" + junitVersion = "4.13.2" + testNgVersion = "7.5" + mockitoVersion = "4.11.0" + jmhLibVersion = "1.21" + guavaVersion = "33.5.0-jre" +} + +def releaseTag = System.getenv("BUILD_TAG") if (releaseTag != null && !releaseTag.isEmpty()) { if (releaseTag.startsWith("v")) { - releaseTag = releaseTag.substring(1); + releaseTag = releaseTag.substring(1) } - version = releaseTag; - project.properties.put("release.version", releaseTag); + project.version = releaseTag - println("Releasing with version " + version); + logger.lifecycle("Releasing with version: " + project.version) } -description = "RxJava: Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM." - -apply plugin: "java-library" -apply plugin: "checkstyle" -apply plugin: "jacoco" -apply plugin: "ru.vyarus.animalsniffer" -apply plugin: "maven" -apply plugin: "me.champeau.gradle.jmh" -apply plugin: "com.github.hierynomus.license" -apply plugin: "com.jfrog.bintray" -apply plugin: "com.jfrog.artifactory" -apply plugin: "eclipse" - -sourceCompatibility = JavaVersion.VERSION_1_6 -targetCompatibility = JavaVersion.VERSION_1_6 - repositories { - mavenCentral() + mavenCentral() } dependencies { - signature "org.codehaus.mojo.signature:java16:1.1@signature" + signature "org.codehaus.mojo.signature:java18:1.0@signature" api "org.reactivestreams:reactive-streams:$reactiveStreamsVersion" jmh "org.reactivestreams:reactive-streams:$reactiveStreamsVersion" @@ -87,8 +50,27 @@ dependencies { testImplementation "com.google.guava:guava:$guavaVersion" } +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.withType(JavaCompile) { + options.compilerArgs << "-parameters" +} + +apply from: file("gradle/javadoc_cleanup.gradle") + javadoc { - failOnError = false exclude "**/internal/**" exclude "**/test/**" exclude "**/perf/**" @@ -100,116 +82,57 @@ javadoc { options.addStringOption("top").value = "" options.addStringOption("doctitle").value = "" options.addStringOption("header").value = "" - options.stylesheetFile = new File(projectDir, "gradle/stylesheet.css"); + options.stylesheetFile = project.file("gradle/stylesheet.css") options.links( - "https://docs.oracle.com/javase/7/docs/api/", - "http://www.reactive-streams.org/reactive-streams-${reactiveStreamsVersion}-javadoc/" + "https://docs.oracle.com/javase/8/docs/api/", + "https://reactivex.io/RxJava/org.reactivestreams.javadoc/${reactiveStreamsVersion}/" ) - if (JavaVersion.current().isJava7()) { - // "./gradle/stylesheet.css" only supports Java 7 - options.addStringOption("stylesheetfile", rootProject.file("./gradle/stylesheet.css").toString()) - } + finalizedBy javadocCleanup } animalsniffer { annotation = "io.reactivex.rxjava3.internal.util.SuppressAnimalSniffer" } -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = "sources" - from sourceSets.main.allSource +moduleConfig { + moduleInfoPath = 'src/main/module/module-info.java' + multiReleaseVersion = 9 + version = project.version } -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = "javadoc" - from javadoc.destinationDir -} - -artifacts { - archives jar - archives sourcesJar - archives javadocJar -} - -apply plugin: 'biz.aQute.bnd.builder' - jar { - 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', - 'Automatic-Module-Name': 'io.reactivex.rxjava3', - 'Export-Package': '!io.reactivex.rxjava3.internal.*, io.reactivex.rxjava3.*' + from('.') { + include 'LICENSE' + include 'COPYRIGHT' + into('META-INF/') + } + 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 rootProject.file("HEADER") + header project.file("config/license/HEADER") ext.year = Calendar.getInstance().get(Calendar.YEAR) skipExistingHeaders true ignoreFailures true excludes(["**/*.md", "**/*.txt"]) } -apply plugin: "maven-publish" - -install { - repositories.mavenInstaller.pom.project { - name "RxJava" - description "Reactive Extensions for Java" - url "https://github.com/ReactiveX/RxJava" - licenses { - license { - name "The Apache Software License, Version 2.0" - url "http://www.apache.org/licenses/LICENSE-2.0.txt" - distribution "repo" - } - } - developers { - developer { - id "akarnokd" - name "David Karnok" - email "akarnokd@gmail.com" - } - } - scm { - connection "scm:git:git@github.com:ReactiveX/RxJava.git" - url "scm:git:git@github.com:ReactiveX/RxJava.git" - developerConnection "scm:git:git@github.com:ReactiveX/RxJava.git" - } - issueManagement { - system "github" - url "https://github.com/ReactiveX/RxJava/issues" - } - } -} - -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact (sourcesJar) { - classifier = "sources" - } - } - } -} - -// Reactive-Streams as compile dependency -publishing.publications.all { - pom.withXml { - asNode().dependencies."*".findAll() { - it.scope.text() == "runtime" && project.configurations.compile.allDependencies.find { dep -> - dep.name == it.artifactId.text() - } - }.each { it.scope*.value = "compile"} - } -} - jmh { jmhVersion = jmhLibVersion humanOutputFile = null @@ -218,162 +141,79 @@ jmh { jvmArgsAppend = ["-Djmh.separateClasspathJAR=true"] if (project.hasProperty("jmh")) { - include = ".*" + project.jmh + ".*" - println("JMH: " + include); + includes = [".*" + project.jmh + ".*"] + logger.info("JMH: {}", includes) } +} +test { + maxHeapSize = "1200m" } -plugins.withType(EclipsePlugin) { - project.eclipse.classpath.plusConfigurations += [ configurations.jmh ] +task testNG(type: Test) { + useTestNG() } -test { - - testLogging { - // showing skipped occasionally should prevent CI timeout due to lack of standard output - events=["skipped", "failed"] // "started", "passed" - // showStandardStreams = true - exceptionFormat="full" +check.dependsOn testNG + +tasks.withType(Test) { + testLogging { + events = ["skipped", "failed"] + exceptionFormat = "full" debug.events = ["skipped", "failed"] - debug.exceptionFormat="full" + debug.exceptionFormat = "full" info.events = ["failed", "skipped"] - info.exceptionFormat="full" - + info.exceptionFormat = "full" + warn.events = ["failed", "skipped"] - warn.exceptionFormat="full" + warn.exceptionFormat = "full" } - maxHeapSize = "1200m" - if (System.getenv("CI") == null) { maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 } } -task testng(type: Test) { - useTestNG() - 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" - } -} - -check.dependsOn testng - -jacoco { - toolVersion = jacocoVersion // See http://www.eclemma.org/jacoco/. -} - -task GCandMem(dependsOn: "check") doLast { - print("Memory usage before: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) - System.gc() - Thread.sleep(200) - print("Memory usage: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) -} - -task GCandMem2(dependsOn: "test") doLast { - print("Memory usage before: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) - System.gc() - Thread.sleep(200) - print("Memory usage: ") - println(java.lang.management.ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0) -} - -testng.dependsOn GCandMem2 - jacocoTestReport { + dependsOn test + dependsOn testNG + reports { - xml.enabled = true - html.enabled = true + xml.required.set(true) + csv.required.set(false) + html.required.set(true) } } -jacocoTestReport.dependsOn GCandMem - -build.dependsOn jacocoTestReport +check.dependsOn jacocoTestReport checkstyle { - configFile file("checkstyle.xml") - ignoreFailures = true - toolVersion = checkstyleVersion + 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' } -if (rootProject.hasProperty("releaseMode")) { - - if ("branch".equals(rootProject.releaseMode)) { - // From https://github.com/ReactiveX/RxAndroid/blob/2.x/rxandroid/build.gradle#L94 - - println("ReleaseMode: " + rootProject.releaseMode); - artifactory { - contextUrl = "https://oss.jfrog.org" - - publish { - repository { - repoKey = "oss-snapshot-local" - - username = rootProject.bintrayUser - password = rootProject.bintrayKey - } - - defaults { - publishConfigs("archives") - } - } +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) + } } - - build.finalizedBy(artifactoryPublish) } + mavenPublishing { + // or when publishing to https://central.sonatype.com/ + publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.CENTRAL_PORTAL) - if ("full".equals(rootProject.releaseMode)) { - // based on https://github.com/bintray/gradle-bintray-plugin - def rver = version; - - println("ReleaseMode: " + rootProject.releaseMode + " version " + rver); - - bintray { - user = rootProject.bintrayUser - key = rootProject.bintrayKey - configurations = ["archives"] - publish = true - pkg { - repo = "RxJava" - name = "RxJava" - userOrg = "reactivex" - labels = ["rxjava", "reactivex"] - licenses = ["Apache-2.0"] - vcsUrl = "https://github.com/ReactiveX/RxJava.git" - version { - name = rver - gpg { - sign = true - } - mavenCentralSync { - sync = true - user = rootProject.sonatypeUsername - password = rootProject.sonatypePassword - close = "1" - } - } - } - } - - build.finalizedBy(bintrayUpload) - } + // signAllPublications() + } } - -apply from: file("gradle/javadoc_cleanup.gradle") diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 3e7ba879da..0000000000 --- a/checkstyle.xml +++ /dev/null @@ -1,43 +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/HEADER b/config/license/HEADER similarity index 100% rename from HEADER rename to config/license/HEADER 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 index 85e7d47077..4badd81308 100644 --- a/docs/Additional-Reading.md +++ b/docs/Additional-Reading.md @@ -3,7 +3,7 @@ A more complete and up-to-date list of resources can be found at the [reactivex. # 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](http://docs.couchbase.com/developer/java-2.0/observables.html) from the Couchbase documentation **(1.x)** +* [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 diff --git a/docs/Backpressure-(2.0).md b/docs/Backpressure-(2.0).md index 61361d21c4..6b2f2860af 100644 --- a/docs/Backpressure-(2.0).md +++ b/docs/Backpressure-(2.0).md @@ -172,7 +172,7 @@ If some of the values can be safely ignored, one can use the sampling (with time } ``` -Note hovewer that these operators only reduce the rate of value reception by the downstream and thus they may still lead to `MissingBackpressureException`. +Note however that these operators only reduce the rate of value reception by the downstream and thus they may still lead to `MissingBackpressureException`. ## onBackpressureBuffer() @@ -229,7 +229,7 @@ Note that the last two strategies cause discontinuity in the stream as they drop ## onBackpressureDrop() -Whenever the downstream is not ready to receive values, this operator will drop that elemenet from the sequence. One can think of it as a 0 capacity `onBackpressureBuffer` with strategy `ON_OVERFLOW_DROP_LATEST`. +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. diff --git a/docs/Backpressure.md b/docs/Backpressure.md index 8feec0d487..8529ec0995 100644 --- a/docs/Backpressure.md +++ b/docs/Backpressure.md @@ -20,7 +20,7 @@ Cold Observables are ideal for the reactive pull model of backpressure described 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. @@ -33,26 +33,26 @@ The following diagrams show how you could use each of these operators on the bur ### sample (or throttleLast) The `sample` operator periodically "dips" into the sequence and emits only the most recently emitted item during each dip: -​ -````groovy +​ +```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": -​ -````groovy +​ +```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: -​ -````groovy +​ +```java Observable burstyDebounced = bursty.debounce(10, TimeUnit.MILLISECONDS); -```` +``` ## Buffers and windows @@ -64,15 +64,15 @@ The following diagrams show how you could use each of these operators on the bur You could, for example, close and emit a buffer of items from the bursty Observable periodically, at a regular interval of time: -​ -````groovy +​ +```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: -​ -````groovy +​ +```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(); @@ -86,17 +86,17 @@ Observable> burstyBuffered = burstyMulticast.buffer(burstyDebounce `window` is similar to `buffer`. One variant of `window` allows you to periodically emit Observable windows of items at a regular interval of time: -​ -````groovy +​ +```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: -​ -````groovy +​ +```java Observable> burstyWindowed = bursty.window(5); -```` +``` # Callstack blocking as a flow-control alternative to backpressure @@ -110,8 +110,8 @@ When you subscribe to an `Observable` with a `Subscriber`, you can request react 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() { +```java +someObservable.subscribe(new Subscriber() { @Override public void onStart() { request(1); @@ -128,13 +128,13 @@ someObservable.subscribe(new Subscriber() { } @Override - public void onNext(t n) { + 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. @@ -158,18 +158,18 @@ For this to work, though, Observables _A_ and _B_ must respond correctly to the
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​
+
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
+
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
+
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 are providing the expected behavior, [one can write custom operators in RxJava](https://github.com/ReactiveX/RxJava/wiki/Implementing-custom-operators-(draft)). +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 index 64d6e1b40a..fe2a640f49 100644 --- a/docs/Blocking-Observable-Operators.md +++ b/docs/Blocking-Observable-Operators.md @@ -18,7 +18,7 @@ To transform an `Observable` into a `BlockingObservable`, use the [`Observable.t > 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` diff --git a/docs/Combining-Observables.md b/docs/Combining-Observables.md index bcc19f6b0f..fcde91b76b 100644 --- a/docs/Combining-Observables.md +++ b/docs/Combining-Observables.md @@ -2,13 +2,13 @@ This section explains operators you can use to combine multiple Observables. # Outline -- [`combineLatest`](#combineLatest) +- [`combineLatest`](#combinelatest) - [`join` and `groupJoin`](#joins) - [`merge`](#merge) -- [`mergeDelayError`](#mergeDelayError) +- [`mergeDelayError`](#mergedelayerror) - [`rxjava-joins`](#rxjava-joins) -- [`startWith`](#startWith) -- [`switchOnNext`](#switchOnNext) +- [`startWith`](#startwith) +- [`switchOnNext`](#switchonnext) - [`zip`](#zip) ## startWith diff --git a/docs/Conditional-and-Boolean-Operators.md b/docs/Conditional-and-Boolean-Operators.md index f7ed64ce34..e6d1358e31 100644 --- a/docs/Conditional-and-Boolean-Operators.md +++ b/docs/Conditional-and-Boolean-Operators.md @@ -1,21 +1,169 @@ This section explains operators with which you conditionally emit or transform Observables, or can do boolean evaluations of them: ### Conditional Operators -* [**`amb( )`**](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 -* [**`defaultIfEmpty( )`**](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 -* (`rxjava-computation-expressions`) [**`doWhile( )`**](http://reactivex.io/documentation/operators/repeat.html) — emit the source Observable's sequence, and then repeat the sequence as long as a condition remains true -* (`rxjava-computation-expressions`) [**`ifThen( )`**](http://reactivex.io/documentation/operators/defer.html) — only emit the source Observable's sequence if a condition is true, otherwise emit an empty or default sequence -* [**`skipUntil( )`**](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 -* [**`skipWhile( )`**](http://reactivex.io/documentation/operators/skipwhile.html) — discard items emitted by an Observable until a specified condition is false, then emit the remainder -* (`rxjava-computation-expressions`) [**`switchCase( )`**](http://reactivex.io/documentation/operators/defer.html) — emit the sequence from a particular Observable based on the results of an evaluation -* [**`takeUntil( )`**](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 -* [**`takeWhile( )` and `takeWhileWithIndex( )`**](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 -* (`rxjava-computation-expressions`) [**`whileDo( )`**](http://reactivex.io/documentation/operators/repeat.html) — if a condition is true, emit the source Observable's sequence and then repeat the sequence as long as the condition remains true - -> (`rxjava-computation-expressions`) — indicates that this operator is currently part of the optional `rxjava-computation-expressions` package under `rxjava-contrib` and is not included with the standard RxJava set of 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 -* [**`all( )`**](http://reactivex.io/documentation/operators/all.html) — determine whether all items emitted by an Observable meet some criteria -* [**`contains( )`**](http://reactivex.io/documentation/operators/contains.html) — determine whether an Observable emits a particular item or not -* [**`exists( )` and `isEmpty( )`**](http://reactivex.io/documentation/operators/contains.html) — determine whether an Observable emits any items or not -* [**`sequenceEqual( )`**](http://reactivex.io/documentation/operators/sequenceequal.html) — test the equality of the sequences emitted by two Observables + +### 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 index a048547529..157ded821b 100644 --- a/docs/Connectable-Observable-Operators.md +++ b/docs/Connectable-Observable-Operators.md @@ -7,25 +7,22 @@ This section explains the [`ConnectableObservable`](http://reactivex.io/RxJava/j 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:** -```groovy -def firstMillion = Observable.range( 1, 1000000 ).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS); +```java +Observable firstMillion = Observable.range(1, 1000000).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS); -firstMillion.subscribe( - { println("Subscriber #1:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #1 complete"); } // onCompleted -); - -firstMillion.subscribe( - { println("Subscriber #2:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #2 complete"); } // onCompleted -); +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 @@ -40,20 +37,18 @@ Subscriber #2:826996 Sequence #2 complete ``` **Example #2:** -```groovy -def firstMillion = Observable.range( 1, 1000000 ).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS).publish(); +```java +ConnectableObservable firstMillion = Observable.range(1, 1000000).sample(7, java.util.concurrent.TimeUnit.MILLISECONDS).publish(); -firstMillion.subscribe( - { println("Subscriber #1:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #1 complete"); } // onCompleted -); +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( - { println("Subscriber #2:" + it); }, // onNext - { println("Error: " + it.getMessage()); }, // onError - { println("Sequence #2 complete"); } // onCompleted -); +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(); ``` diff --git a/docs/Filtering-Observables.md b/docs/Filtering-Observables.md index 620800dc8c..512b69ba8a 100644 --- a/docs/Filtering-Observables.md +++ b/docs/Filtering-Observables.md @@ -259,7 +259,7 @@ firstOrError.subscribe( **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 the source. +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 diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index fb9baa47dd..69b69a8ca6 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -1,20 +1,20 @@ ## Getting Binaries -You can find binaries and dependency information for Maven, Ivy, Gradle, SBT, and others at [http://search.maven.org](http://search.maven.org/#search%7Cga%7C1%7Cg%3A"io.reactivex.rxjava2"%20AND%20"rxjava2"). +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.rxjava2 + io.reactivex.rxjava3 rxjava - 2.2.0 + 3.0.4 ``` and for Ivy: ```xml - + ``` and for SBT: @@ -22,12 +22,12 @@ and for SBT: ```scala libraryDependencies += "io.reactivex" %% "rxscala" % "0.26.5" -libraryDependencies += "io.reactivex.rxjava2" % "rxjava" % "2.2.0" +libraryDependencies += "io.reactivex.rxjava3" % "rxjava" % "3.0.4" ``` and for Gradle: ```groovy -compile 'io.reactivex.rxjava2:rxjava:2.2.0' +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: @@ -38,17 +38,17 @@ If you need to download the jars instead of using a build system, create a Maven xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - io.reactivex.rxjava2 + io.reactivex.rxjava3 rxjava - 2.2.0 + 3.0.4 RxJava Reactive Extensions for Java https://github.com/ReactiveX/RxJava - io.reactivex.rxjava2 + io.reactivex.rxjava3 rxjava - 2.2.0 + 3.0.4 @@ -66,18 +66,21 @@ You need Java 6 or later. ### Snapshots -Snapshots are available via [JFrog](https://oss.jfrog.org/libs-snapshot/io/reactivex/rxjava2/rxjava/): +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.jfrog.org/libs-snapshot' } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } dependencies { - compile 'io.reactivex.rxjava2:rxjava:2.2.0-SNAPSHOT' + 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: diff --git a/docs/How-To-Use-RxJava.md b/docs/How-To-Use-RxJava.md index 16d156caa9..41a46bb75d 100644 --- a/docs/How-To-Use-RxJava.md +++ b/docs/How-To-Use-RxJava.md @@ -96,9 +96,9 @@ You use the Observable [`just( )`](http://reactivex.io/documentation/operators Observable o = Observable.from("a", "b", "c"); def list = [5, 6, 7, 8] -Observable o = Observable.from(list); +Observable o2 = Observable.from(list); -Observable o = Observable.just("one object"); +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. @@ -285,7 +285,7 @@ onNext => value_14_xform Here is a marble diagram that illustrates this transformation: - + 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): @@ -333,7 +333,7 @@ The response looks like this: And here is a marble diagram that illustrates how that code produces that response: - + 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: @@ -350,7 +350,7 @@ public Observable getVideoSummary(APIVideo video) { 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: - + ## Error Handling 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) | +-----|---|---|---|---|---| +`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) ([1](#notes-1))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([1](#notes-1))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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)| +`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)| +`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)| +`andThen`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([3](#notes-3))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([3](#notes-3))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([3](#notes-3))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([3](#notes-3))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([1](#notes-1))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([1](#notes-1))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`blockingAwait`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([4](#notes-4))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([4](#notes-4))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([5](#notes-5))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([5](#notes-5))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([8](#notes-8))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([8](#notes-8))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([8](#notes-8))| +`blockingGet`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([4](#notes-4))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([4](#notes-4))|![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) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([6](#notes-6))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([7](#notes-7))| +`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)| +`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) ([9](#notes-9))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([10](#notes-10))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([11](#notes-11))| +`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)| +`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) ([12](#notes-12))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([12](#notes-12))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([12](#notes-12))| +`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) ([2](#notes-2))| +`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) ([13](#notes-13))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([14](#notes-14))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([15](#notes-15))| +`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) ([13](#notes-13))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([14](#notes-14))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([15](#notes-15))| +`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) ([16](#notes-16))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([16](#notes-16))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([17](#notes-17))| +`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) ([18](#notes-18))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([18](#notes-18))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([19](#notes-19))| +`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) ([18](#notes-18))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([18](#notes-18))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([20](#notes-20))| +`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) ([16](#notes-16))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([16](#notes-16))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([21](#notes-21))| +`complete`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([22](#notes-22))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([22](#notes-22))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([22](#notes-22))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([23](#notes-23))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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)| +`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)| +`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)| +`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)| +`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) ([24](#notes-24))| +`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) ([25](#notes-25))| +`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)| +`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) ([26](#notes-26))| +`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) ([27](#notes-27))| +`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) ([28](#notes-28))| +`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) ([28](#notes-28))| +`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) ([29](#notes-29))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([29](#notes-29))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([30](#notes-30))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([30](#notes-30))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([31](#notes-31))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([31](#notes-31))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([31](#notes-31))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([31](#notes-31))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([32](#notes-32))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([32](#notes-32))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([33](#notes-33))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([34](#notes-34))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([34](#notes-34))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([35](#notes-35))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([36](#notes-36))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([36](#notes-36))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([37](#notes-37))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([37](#notes-37))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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)| +`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) ([2](#notes-2))| +`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) ([38](#notes-38))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([39](#notes-39))| +`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)| +`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) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([23](#notes-23))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([42](#notes-42))| +`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)| +`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)| +`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)| +`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) ([41](#notes-41))| +`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) ([43](#notes-43))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([43](#notes-43))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([43](#notes-43))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([43](#notes-43))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([44](#notes-44))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([44](#notes-44))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`doAfterSuccess`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([45](#notes-45))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([45](#notes-45))|![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) ([41](#notes-41))| +`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)| +`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)| +`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) ([46](#notes-46))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([46](#notes-46))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([46](#notes-46))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([46](#notes-46))| +`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) ([47](#notes-47))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`doOnDispose`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([48](#notes-48))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([49](#notes-49))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([49](#notes-49))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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)| +`doOnEvent`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([50](#notes-50))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([50](#notes-50))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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)| +`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) ([51](#notes-51))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([51](#notes-51))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))| +`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)| +`doOnSuccess`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([53](#notes-53))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([53](#notes-53))|![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) ([41](#notes-41))| +`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)| +`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) ([54](#notes-54))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([55](#notes-55))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([56](#notes-56))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([55](#notes-55))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([23](#notes-23))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([57](#notes-57))| +`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)| +`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) ([41](#notes-41))| +`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) ([58](#notes-58))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([59](#notes-59))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([42](#notes-42))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([61](#notes-61))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([61](#notes-61))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([62](#notes-62))| +`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) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([64](#notes-64))| +`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) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))| +`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) ([28](#notes-28))| +`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) ([28](#notes-28))| +`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) ([32](#notes-32))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([32](#notes-32))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([65](#notes-65))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`flatMapObservable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([66](#notes-66))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([67](#notes-67))|![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) ([28](#notes-28))| +`flatMapPublisher`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([67](#notes-67))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([68](#notes-68))|![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) ([28](#notes-28))| +`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) ([65](#notes-65))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([37](#notes-37))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([37](#notes-37))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`flattenAsFlowable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([69](#notes-69))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([69](#notes-69))|![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) ([28](#notes-28))| +`flattenAsObservable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([69](#notes-69))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([69](#notes-69))|![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) ([28](#notes-28))| +`flattenStreamAsFlowable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([70](#notes-70))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([70](#notes-70))|![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) ([28](#notes-28))| +`flattenStreamAsObservable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([70](#notes-70))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([70](#notes-70))|![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) ([28](#notes-28))| +`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) ([71](#notes-71))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([71](#notes-71))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([71](#notes-71))| +`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) ([71](#notes-71))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([71](#notes-71))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([71](#notes-71))| +`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) ([23](#notes-23))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([72](#notes-72))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([73](#notes-73))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([74](#notes-74))| +`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)| +`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) ([75](#notes-75))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([76](#notes-76))| +`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)| +`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)| +`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) ([72](#notes-72))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([73](#notes-73))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([74](#notes-74))| +`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) ([76](#notes-76))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([76](#notes-76))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([73](#notes-73))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([74](#notes-74))| +`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)| +`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) ([23](#notes-23))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([76](#notes-76))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([72](#notes-72))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([73](#notes-73))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([74](#notes-74))| +`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)| +`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) ([77](#notes-77))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([77](#notes-77))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([77](#notes-77))| +`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) ([78](#notes-78))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([78](#notes-78))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([79](#notes-79))| +`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) ([78](#notes-78))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([78](#notes-78))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([80](#notes-80))| +`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)| +`ignoreElement`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([81](#notes-81))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([81](#notes-81))|![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) ([2](#notes-2))| +`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) ([82](#notes-82))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([82](#notes-82))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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) ([83](#notes-83))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([83](#notes-83))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([83](#notes-83))| +`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) ([83](#notes-83))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([83](#notes-83))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([83](#notes-83))| +`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) ([59](#notes-59))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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) ([84](#notes-84))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([84](#notes-84))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([80](#notes-80))| +`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) ([2](#notes-2))| +`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) ([58](#notes-58))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([59](#notes-59))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([42](#notes-42))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([61](#notes-61))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([61](#notes-61))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([62](#notes-62))| +`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) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([64](#notes-64))| +`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) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))| +`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)| +`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) ([28](#notes-28))| +`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) ([28](#notes-28))| +`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)| +`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)| +`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)| +`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)| +`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)| +`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)| +`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)| +`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)| +`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) ([85](#notes-85))| +`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) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))| +`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) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))| +`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) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))| +`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)| +`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)| +`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)| +`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)| +`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)| +`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)| +`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) ([86](#notes-86))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([86](#notes-86))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([86](#notes-86))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([86](#notes-86))| +`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) ([87](#notes-87))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([88](#notes-88))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([89](#notes-89))| +`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) ([90](#notes-90))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([90](#notes-90))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([74](#notes-74))| +`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) ([90](#notes-90))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([90](#notes-90))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([74](#notes-74))| +`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) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([52](#notes-52))| +`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) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([92](#notes-92))| +`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) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([92](#notes-92))| +`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)| +`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)| +`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)| +`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) ([87](#notes-87))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([88](#notes-88))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([89](#notes-89))| +`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)| +`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)| +`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)| +`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)| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([92](#notes-92))| +`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) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([91](#notes-91))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([92](#notes-92))| +`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)| +`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) ([93](#notes-93))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([93](#notes-93))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([93](#notes-93))| +`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) ([87](#notes-87))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([88](#notes-88))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([89](#notes-89))| +`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) ([58](#notes-58))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([59](#notes-59))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([42](#notes-42))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([61](#notes-61))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([61](#notes-61))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([62](#notes-62))| +`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) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([64](#notes-64))| +`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) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([63](#notes-63))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))| +`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) ([94](#notes-94))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([94](#notes-94))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([94](#notes-94))| +`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) ([95](#notes-95))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([95](#notes-95))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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) ([78](#notes-78))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([78](#notes-78))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([78](#notes-78))| +`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)| +`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) ([96](#notes-96))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([96](#notes-96))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([96](#notes-96))| +`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) ([97](#notes-97))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([97](#notes-97))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([97](#notes-97))| +`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) ([98](#notes-98))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([98](#notes-98))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([98](#notes-98))| +`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)| +`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)| +`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)| +`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) ([23](#notes-23))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([99](#notes-99))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([100](#notes-100))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([28](#notes-28))| +`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)| +`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)| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))| +`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) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([60](#notes-60))| +`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)| +`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) ([95](#notes-95))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([95](#notes-95))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([2](#notes-2))| +`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)| +`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) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([40](#notes-40))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([41](#notes-41))| +`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) ([41](#notes-41))| +`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)| +`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)| +`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) ([41](#notes-41))| +`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)| +`toCompletionStage`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([101](#notes-101))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([101](#notes-101))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`toFlowable`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([102](#notes-102))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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)| +`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) ([13](#notes-13))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([14](#notes-14))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([15](#notes-15))| +`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) ([13](#notes-13))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([14](#notes-14))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([15](#notes-15))| +`toMaybe`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([103](#notes-103))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([103](#notes-103))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([102](#notes-102))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([13](#notes-13))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([14](#notes-14))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([15](#notes-15))| +`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) ([102](#notes-102))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`toSingle`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([104](#notes-104))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([104](#notes-104))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([102](#notes-102))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`toSingleDefault`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([105](#notes-105))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([105](#notes-105))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([106](#notes-106))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([102](#notes-102))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([13](#notes-13))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([14](#notes-14))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([15](#notes-15))| +`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)| +`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)| +`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)| +`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) ([107](#notes-107))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([108](#notes-108))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([109](#notes-109))| +`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) ([16](#notes-16))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([16](#notes-16))|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([17](#notes-17))| +`wrap`|![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png) ([110](#notes-110))|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)|![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)| +`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) ([111](#notes-111))| +`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) ([112](#notes-112))| +`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) ([113](#notes-113))| +**237 operators** | **216** | **210** | **118** | **108** | **84** | + +#### Notes +1 Use [`contains()`](#contains).
+2 Always empty.
+3 Use [`concatWith`](#concatWith).
+4 Use [`blockingFirst()`](#blockingFirst), [`blockingSingle()`](#blockingSingle) or [`blockingLast()`](#blockingLast).
+5 Use [`blockingGet()`](#blockingGet).
+6 At most one element to get. Use [`blockingGet()`](#blockingGet).
+7 No elements to get. Use [`blockingAwait()`](#blockingAwait).
+8 Use [`blockingSubscribe()`](#blockingSubscribe)
+9 Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.
+10 Use [`map()`](#map) to transform into a list/collection.
+11 Always empty. Use [`andThen()`](#andThen) to bring in a list/collection.
+12 At most one element to store. Use [`cache()`](#cache).
+13 At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.
+14 One element to collect. Use [`map()`](#map) to transform into a list/collection.
+15 Always empty. Use [`andThen()`](#andThen) to bring in a collection.
+16 At most one element per source. Use [`zip()`](#zip).
+17 Always empty. Use [`merge()`](#merge).
+18 At most one element per source. Use [`zipArray()`](#zipArray).
+19 Always empty. Use [`mergeArray()`](#mergeArray).
+20 Always empty. Use [`mergeArrayDelayError()`](#mergeArrayDelayError).
+21 Always empty. Use [`mergeDelayError()`](#mergeDelayError).
+22 Use [`empty()`](#empty).
+23 Never empty.
+24 No items to keep ordered. Use [`mergeArray()`](#mergeArray).
+25 No items to keep ordered. Use [`mergeArrayDelayError()`](#mergeArrayDelayError).
+26 No items to keep ordered. Use [`merge()`](#merge).
+27 No items to keep ordered. Use [`mergeDelayError()`](#mergeDelayError).
+28 Always empty thus no items to map.
+29 Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapCompletable`](#concatMapCompletable).
+30 Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMap`](#concatMap).
+31 At most one item to map. Use [`concatMap()`](#concatMap).
+32 At most one item. Use [`flattenAsFlowable`](#flattenAsFlowable) or [`flattenAsObservable`](#flattenAsObservable).
+33 Use [`concatMap`](#concatMap).
+34 Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapMaybe`](#concatMapMaybe).
+35 Use [`concatMap()`](#concatMap).
+36 Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapSingle`](#concatMapSingle).
+37 At most one item. Use [`flattenStreamAsFlowable`](#flattenStreamAsFlowable) or [`flattenStreamAsObservable`](#flattenStreamAsObservable).
+38 Never empty thus always 1.
+39 Always empty thus always 0.
+40 At most one item signaled so no subsequent items to work with.
+41 Always empty thus no items to work with.
+42 Always empty. Use [`andThen()`](#andThen) to chose the follow-up sequence.
+43 At most one item, always distinct.
+44 Different terminology. Use [`doAfterSuccess()`](#doAfterSuccess).
+45 Different terminology. Use [`doAfterNext()`](#doAfterNext).
+46 Different terminology. Use [`doOnDispose()`](#doOnDispose).
+47 Always succeeds or fails, there is no `onComplete` signal.
+48 Different terminology. Use [`doOnCancel()`](#doOnCancel).
+49 At most one item. Use [`doOnEvent()`](#doOnEvent).
+50 Use [`doOnEach()`](#doOnEach).
+51 Different terminology. Use [`doOnSuccess()`](#doOnSuccess).
+52 Backpressure related and not supported outside `Flowable`.
+53 Different terminology. Use [`doOnNext()`](#doOnNext).
+54 At most one item with index 0. Use [`defaultIfEmpty`](#defaultIfEmpty).
+55 Always one item with index 0.
+56 At most one item with index 0. Use [`toSingle`](#toSingle).
+57 Use [`complete()`](#complete).
+58 At most one item. Use [`defaultIfEmpty`](#defaultIfEmpty).
+59 Always one item.
+60 At most one item, would be no-op.
+61 Always one item, would be no-op.
+62 Always empty. Use [`andThen()`](#andThen) and [`error()`](#error).
+63 At most one item. Use [`toCompletionStage()`](#toCompletionStage).
+64 Always empty. Use [`andThen()`](#andThen), [`error()`](#error) and [`toCompletionStage()`](#toCompletionStage).
+65 Use [`flatMap()`](#flatMap).
+66 Not supported. Use [`flatMap`](#flatMap) and [`toFlowable()`](#toFlowable).
+67 Use [`flatMap`](#flatMap).
+68 Not supported. Use [`flatMap`](#flatMap) and [`toObservable()`](#toFlowable).
+69 Use [`flatMapIterable()`](#flatMapIterable).
+70 Use [`flatMapStream()`](#flatMapStream).
+71 Use [`subscribe()`](#subscribe).
+72 At most one item. Use [`just()`](#just) or [`empty()`](#empty).
+73 Always one item. Use [`just()`](#just).
+74 Always empty. Use [`complete()`](#complete).
+75 Always error.
+76 Use [`wrap()`](#wrap).
+77 Use [`fromSupplier()`](#fromSupplier).
+78 At most one item.
+79 Always empty thus no items to group.
+80 Always empty thus no items to join.
+81 Use [`ignoreElements()`](#ignoreElements).
+82 Use [`ignoreElement()`](#ignoreElement).
+83 At most one item. Use [`timer()`](#timer).
+84 At most one item. Use [`zip()`](#zip)
+85 Always empty thus no items to filter.
+86 Needs backpressure thus not supported outside `Flowable`.
+87 Connectable sources not supported outside `Flowable` and `Observable`. Use a `MaybeSubject`.
+88 Connectable sources not supported outside `Flowable` and `Observable`. Use a `SingleSubject`.
+89 Connectable sources not supported outside `Flowable` and `Observable`. Use a `ConnectableSubject`.
+90 At most one item. Use [`just()`](#just).
+91 At most one item. Use [`map()`](#map).
+92 Always empty thus no items to reduce.
+93 At most one signal type.
+94 At most one item. Use [`takeUntil()`](#takeUntil).
+95 At most one item. Use [`filter()`](#filter).
+96 Use [`startWith()`](#startWith) and [`fromArray()`](#fromArray) of `Flowable` or `Observable`.
+97 Use [`startWith()`](#startWith) and [`just()`](#just) of another reactive type.
+98 Use [`startWith()`](#startWith) and [`fromIterable()`](#fromArray) of `Flowable` or `Observable`.
+99 Always empty. Use [`defaultIfEmpty()`](#defaultIfEmpty).
+100 At most one item. Use [`flatMap()`](#flatMap).
+101 Use [`firstStage`](#firstStage), [`lastStage`](#lastStage) or [`singleStage`](#singleStage).
+102 Would be no-op.
+103 Use [`firstElement`](#firstElement), [`lastElement`](#lastElement) or [`singleElement`](#singleElement).
+104 Use [`firstOrError`](#firstOrError), [`lastOrError`](#lastOrError) or [`singleOrError`](#singleOrError).
+105 Use [`first`](#first), [`last`](#last) or [`single`](#single).
+106 Use [`defaultIfEmpty()`](#defaultIfEmpty).
+107 Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a nested source.
+108 Use [`map()`](#map) to transform into a nested source.
+109 Always empty. Use [`andThen()`](#andThen) to bring in a nested source.
+110 Use [`fromPublisher()`](#fromPublisher).
+111 Use [`merge()`](#merge).
+112 Use [`mergeArray()`](#mergeArray).
+113 Use [`mergeWith()`](#mergeWith).
+ +#### Under development + +*Currently, all intended operators are implemented.* diff --git a/docs/Phantom-Operators.md b/docs/Phantom-Operators.md index 60da4a1a40..5193147a22 100644 --- a/docs/Phantom-Operators.md +++ b/docs/Phantom-Operators.md @@ -19,7 +19,7 @@ These operators have been proposed but are not part of the 1.0 release of RxJava ## chunkify( ) #### returns an iterable that periodically returns a list of items emitted by the source Observable since the last list - + 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. @@ -27,7 +27,7 @@ The `chunkify( )` operator represents a blocking observable as an Iterable, th ## fromFuture( ) #### convert a Future into an Observable, but do not attempt to get the Future's value until a Subscriber subscribes - + 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. @@ -35,7 +35,7 @@ The `fromFuture( )` method also converts a Future into an Observable, but it o ## forEachFuture( ) #### create a futureTask that will invoke a specified function on each item emitted by an Observable - + 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). @@ -43,7 +43,7 @@ The `forEachFuture( )` returns a `FutureTask` for each item emitted by the sou ## forIterable( ) #### apply a function to the elements of an Iterable to create Observables which are then concatenated - + `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. @@ -58,7 +58,7 @@ If the a subscriber to the Observable that results when a Future is converted to ## generate( ) and generateAbsoluteTime( ) #### create an Observable that emits a sequence of items as generated by a function of your choosing - + 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. @@ -66,7 +66,7 @@ The basic form of `generate( )` takes four parameters. These are `initialState 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). - + #### see also: * Introduction to Rx: Generate @@ -79,7 +79,7 @@ There are also versions of `generate( )` that allow you to do the work of gene 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. -​ +​ 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). @@ -101,7 +101,7 @@ To represent an Observable as a Connectable Observable, use the `multicast( )` ## onErrorFlatMap( ) #### instructs an Observable to emit a sequence of items whenever it encounters an error -​ +​ 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. @@ -111,13 +111,13 @@ Because `onErrorFlatMap( )` is designed to work with pathological source Obser 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( )`: -​ +​ *** ## parallel( ) #### split the work done on the emissions from an Observable into multiple Observables each operating on its own parallel thread -​ +​ 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. @@ -127,7 +127,7 @@ 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 asychronous calls. +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: * RxJava Threading Examples by Graham Lea @@ -136,7 +136,7 @@ Kick off your work for each item inside [`flatMap`](Transforming-Observables#fla ## parallelMerge( ) #### combine multiple Observables into a smaller number of Observables, to facilitate parallelism -​ +​ 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. @@ -144,7 +144,7 @@ Use the `parallelMerge( )` method to take an Observable that emits a large num ## pivot( ) #### combine multiple sets of grouped observables so that they are arranged primarily by group rather than by set -​ +​ 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. @@ -152,13 +152,13 @@ An example may make this clearer. Imagine you use `groupBy( )` to group the em 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: -​ +​ *** ## publishLast( ) #### represent an Observable as a Connectable Observable that emits only the last item emitted by the source Observable - + #### see also: * RxJS: `publishLast` diff --git a/docs/What's-different-in-2.0.md b/docs/What's-different-in-2.0.md index fac50df56d..edc681fa7f 100644 --- a/docs/What's-different-in-2.0.md +++ b/docs/What's-different-in-2.0.md @@ -450,12 +450,12 @@ Before 2.0.7, the operator `strict()` had to be applied in order to achieve the 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 resposibility of `FlowableSubscriber` to ensure thread-safety between the remaining instructions in `onSubscribe` and `onNext`. + - §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 the `subscribe` methods other than `Flowable.subscribe(Subscriber)`, there is no need to do anything regarding this change and there is no extra penalty for it. +From a user's perspective, if one was using the `subscribe` methods other than `Flowable.subscribe(Subscriber)`, 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)` 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. diff --git a/docs/Writing-operators-for-2.0.md b/docs/Writing-operators-for-2.0.md index 7b6e57666e..1a51664880 100644 --- a/docs/Writing-operators-for-2.0.md +++ b/docs/Writing-operators-for-2.0.md @@ -1,7 +1,7 @@ ##### Table of contents - [Introduction](#introduction) - - [Warning on internal components](warning-on-internal-components) + - [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) @@ -565,7 +565,7 @@ Version 2.0.7 introduced a new interface, `FlowableSubscriber` that extends `Sub 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 resposibility of `FlowableSubscriber` to ensure thread-safety between the remaining instructions in `onSubscribe` and `onNext`. +- §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`. diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md index 1fe0339407..22961acec3 100644 --- a/docs/_Sidebar.md +++ b/docs/_Sidebar.md @@ -21,11 +21,12 @@ * [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) +* 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 index 1fe0339407..32e201fc46 100644 --- a/docs/_Sidebar.md.md +++ b/docs/_Sidebar.md.md @@ -21,6 +21,7 @@ * [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) diff --git a/gradle.properties b/gradle.properties index 820b2a5bc1..e685b8103a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,24 @@ -release.scope=patch -release.version=3.0.0-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/buildViaTravis.sh b/gradle/buildViaTravis.sh deleted file mode 100755 index ea385c3e92..0000000000 --- a/gradle/buildViaTravis.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# This script will build the project. - -buildTag="$TRAVIS_TAG" - -if [ "$buildTag" != "" ] && [ "${buildTag:0:3}" != "v3." ]; then - echo -e "Wrong tag on the 3.x brach: $buildTag : build stopped" - exit 1 -fi - -export GRADLE_OPTS=-Xmx1024m - -if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then - echo -e "Build Pull Request #$TRAVIS_PULL_REQUEST => Branch [$TRAVIS_BRANCH]" - ./gradlew -PreleaseMode=pr build --stacktrace -elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" == "" ]; then - if [ "$TRAVIS_BRANCH" != "3.x" ]; then - echo -e 'Build secondary Branch (no snapshot) => Branch ['$TRAVIS_BRANCH']' - ./gradlew -PreleaseMode=pr build --stacktrace - else - echo -e 'Build Branch with Snapshot => Branch ['$TRAVIS_BRANCH']' - ./gradlew -PreleaseMode=branch -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace - fi -elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then - echo -e 'Build Branch for Release => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG']' - ./gradlew -PreleaseMode=full -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build --stacktrace -else - echo -e 'WARN: Should not be here => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG'] Pull Request ['$TRAVIS_PULL_REQUEST']' -fi diff --git a/gradle/javadoc_cleanup.gradle b/gradle/javadoc_cleanup.gradle index 12216464a7..63b4f7f045 100644 --- a/gradle/javadoc_cleanup.gradle +++ b/gradle/javadoc_cleanup.gradle @@ -1,36 +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/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/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/plugins/RxJavaPlugins.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/parallel/ParallelFlowable.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) { - println("Cleaning up: " + 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 "); + fileContents = fileContents.replaceAll(",\\s{4,}", ",\n ") // lots of spaces after the @NonNull annotations - fileContents = fileContents.replaceAll("@NonNull\\s{4,}", "@NonNull "); + fileContents = fileContents.replaceAll("@NonNull\\s{4,}", "@NonNull ") // lots of spaces after the @Nullable annotations - fileContents = fileContents.replaceAll("@Nullable\\s{4,}", "@Nullable "); + fileContents = fileContents.replaceAll("@Nullable\\s{4,}", "@Nullable ") - file.setText(fileContents, 'UTF-8'); -} + // javadoc bug: duplicates the link to @NonNull for some reason + def nonNullText1 = "@NonNull" + + fileContents = fileContents.replace(nonNullText1 + " " + nonNullText1, nonNullText1) + fileContents = fileContents.replace(nonNullText1 + "\n " + nonNullText1, nonNullText1) + fileContents = fileContents.replace(nonNullText1 + "\r\n " + nonNullText1, nonNullText1) + + def nonNullText2 = "@NonNull" + fileContents = fileContents.replace(nonNullText2 + " " + nonNullText2, nonNullText2) + fileContents = fileContents.replace(nonNullText2 + "\n " + nonNullText2, nonNullText2) + fileContents = fileContents.replace(nonNullText2 + "\r\n " + nonNullText2, nonNullText2) -javadocJar.dependsOn javadocCleanup -build.dependsOn javadocCleanup \ No newline at end of file + // javadoc bug: duplicates the link to @Nullable for some reason + def nullableText1 = "@Nullable" + + fileContents = fileContents.replace(nullableText1 + " " + nullableText1, nullableText1) + fileContents = fileContents.replace(nullableText1 + "\n " + nullableText1, nullableText1) + fileContents = fileContents.replace(nullableText1 + "\r\n " + nullableText1, nullableText1) + + def nullableText2 = "@Nullable" + + 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/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016..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 94920145f3..3c44eb1b6f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfdc3..65dcd68d65 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# 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. @@ -17,78 +17,113 @@ # ############################################################################## -## -## 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/. +# ############################################################################## # 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 +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 -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -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 () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac 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 @@ -97,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 @@ -105,84 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "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" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # 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 +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # 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 -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# 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 -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# 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. +# -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +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 9618d8d960..93e3f59f13 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,10 +25,14 @@ if "%OS%"=="Windows_NT" setlocal 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" @@ -37,7 +41,7 @@ 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. @@ -51,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% @@ -61,38 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_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=%* - :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/gradle/push_javadoc.sh b/push_javadoc.sh similarity index 78% rename from gradle/push_javadoc.sh rename to push_javadoc.sh index c8f648258e..28ce74f1db 100644 --- a/gradle/push_javadoc.sh +++ b/push_javadoc.sh @@ -8,21 +8,9 @@ targetRepo=github.com/ReactiveX/RxJava.git # ======================================================================= -# only for main pushes, for now -if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then - echo -e "Pull request detected, skipping JavaDocs pushback." - exit 0 -fi - -# only when on the 3.x branch and not tagged -if [ "$TRAVIS_BRANCH" != "3.x" ] && [ "$TRAVIS_TAG" == "" ]; then - echo -e "On a secondary branch '$TRAVIS_BRANCH', skipping JavaDocs pushback." - exit 0 -fi - # get the current build tag if any -buildTag="$TRAVIS_TAG" -echo -e "Travis tag: '$buildTag'" +buildTag="$BUILD_TAG" +echo -e "Build tag: '$buildTag'" if [ "$buildTag" == "" ]; then buildTag="snapshot" @@ -33,18 +21,18 @@ fi echo -e "JavaDocs pushback for tag: $buildTag" # check if the token is actually there -if [ "$GITHUB_TOKEN" == "" ]; then +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 "travis@travis-ci.org" -git config --global user.name "Travis CI" +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://${GITHUB_TOKEN}@${targetRepo} > /dev/null 2>&1 +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" @@ -119,8 +107,8 @@ echo -e "Removing deleted files" git add -u # commit all -echo -e "commit Travis build: $TRAVIS_BUILD_NUMBER for $buildTag" -git commit --message "Travis build: $TRAVIS_BUILD_NUMBER for $buildTag" +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" diff --git a/src/jmh/java/io/reactivex/rxjava3/core/BinaryFlatMapPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/BinaryFlatMapPerf.java index 6d5bb44b23..6e6ae7e3c6 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/BinaryFlatMapPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/BinaryFlatMapPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -86,48 +86,42 @@ public void setup() { singleFlatMapPublisher = Single.just(1).flatMapPublisher(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return arrayFlowable; } }); singleFlatMapHidePublisher = Single.just(1).flatMapPublisher(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return arrayFlowableHide; } }); singleFlattenAsPublisher = Single.just(1).flattenAsFlowable(new Function>() { @Override - public Iterable apply(Integer v) - throws Exception { + public Iterable apply(Integer v) { return list; } }); maybeFlatMapPublisher = Maybe.just(1).flatMapPublisher(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return arrayFlowable; } }); maybeFlatMapHidePublisher = Maybe.just(1).flatMapPublisher(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return arrayFlowableHide; } }); maybeFlattenAsPublisher = Maybe.just(1).flattenAsFlowable(new Function>() { @Override - public Iterable apply(Integer v) - throws Exception { + public Iterable apply(Integer v) { return list; } }); @@ -140,48 +134,42 @@ public Iterable apply(Integer v) singleFlatMapObservable = Single.just(1).flatMapObservable(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return arrayObservable; } }); - singleFlatMapHideObservable = Single.just(1).flatMapObservable(new Function>() { + singleFlatMapHideObservable = Single.just(1).flatMapObservable(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return arrayObservableHide; } }); singleFlattenAsObservable = Single.just(1).flattenAsObservable(new Function>() { @Override - public Iterable apply(Integer v) - throws Exception { + public Iterable apply(Integer v) { return list; } }); - maybeFlatMapObservable = Maybe.just(1).flatMapObservable(new Function>() { + maybeFlatMapObservable = Maybe.just(1).flatMapObservable(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return arrayObservable; } }); - maybeFlatMapHideObservable = Maybe.just(1).flatMapObservable(new Function>() { + maybeFlatMapHideObservable = Maybe.just(1).flatMapObservable(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return arrayObservableHide; } }); maybeFlattenAsObservable = Maybe.just(1).flattenAsObservable(new Function>() { @Override - public Iterable apply(Integer v) - throws Exception { + public Iterable apply(Integer v) { return list; } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/BlockingGetPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/BlockingGetPerf.java index f40a8ca55b..7f3711788a 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/BlockingGetPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/BlockingGetPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/BlockingPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/BlockingPerf.java index 020befc21a..5fb2825b3e 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/BlockingPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/BlockingPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/CallableAsyncPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/CallableAsyncPerf.java index 4afa54ecc8..1bae28f9f9 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/CallableAsyncPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/CallableAsyncPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -68,7 +68,7 @@ public void setup() { Callable c = new Callable() { @Override - public Integer call() throws Exception { + public Integer call() { return 1; } }; @@ -120,71 +120,71 @@ public void subscribeOnFlowable(Blackhole bh) { @Benchmark public void observeOnFlowable(Blackhole bh) { observeOnFlowable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineFlowable(Blackhole bh) { pipelineFlowable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnObservable(Blackhole bh) { subscribeOnObservable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnObservable(Blackhole bh) { observeOnObservable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineObservable(Blackhole bh) { pipelineObservable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnSingle(Blackhole bh) { observeOnSingle.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnSingle(Blackhole bh) { subscribeOnSingle.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineSingle(Blackhole bh) { pipelineSingle.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnCompletable(Blackhole bh) { observeOnCompletable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnCompletable(Blackhole bh) { subscribeOnCompletable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineCompletable(Blackhole bh) { pipelineCompletable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnMaybe(Blackhole bh) { observeOnMaybe.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnMaybe(Blackhole bh) { subscribeOnMaybe.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineMaybe(Blackhole bh) { pipelineMaybe.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } } diff --git a/src/jmh/java/io/reactivex/rxjava3/core/EachTypeFlatMapPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/EachTypeFlatMapPerf.java index e05921c531..14aa8c3605 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/EachTypeFlatMapPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/EachTypeFlatMapPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -114,11 +114,11 @@ public void nbpRangeMapRange(Blackhole bh) { @Benchmark public void singleJust(Blackhole bh) { - singleJust.subscribe(new LatchedSingleObserver(bh)); + singleJust.subscribe(new LatchedSingleObserver<>(bh)); } @Benchmark public void singleJustMapJust(Blackhole bh) { - singleJustMapJust.subscribe(new LatchedSingleObserver(bh)); + singleJustMapJust.subscribe(new LatchedSingleObserver<>(bh)); } } diff --git a/src/jmh/java/io/reactivex/rxjava3/core/FlatMapJustPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/FlatMapJustPerf.java index 8541f92834..597fae480d 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/FlatMapJustPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/FlatMapJustPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,14 +41,14 @@ public void setup() { flowable = Flowable.fromArray(array).flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); observable = Observable.fromArray(array).flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/FlattenCrossMapPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/FlattenCrossMapPerf.java index cfc5a1af97..c5d516423f 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/FlattenCrossMapPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/FlattenCrossMapPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -47,14 +47,14 @@ public void setup() { flowable = Flowable.fromArray(array).flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return list; } }); observable = Observable.fromArray(array).flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return list; } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/FlattenJustPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/FlattenJustPerf.java index bb2394a053..3bb1ad1473 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/FlattenJustPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/FlattenJustPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,14 +44,14 @@ public void setup() { flowable = Flowable.fromArray(array).flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return singletonList; } }); observable = Observable.fromArray(array).flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return singletonList; } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/FlattenRangePerf.java b/src/jmh/java/io/reactivex/rxjava3/core/FlattenRangePerf.java index 663fd8cdde..0339f345c3 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/FlattenRangePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/FlattenRangePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,14 +44,14 @@ public void setup() { flowable = Flowable.fromArray(array).flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return list; } }); observable = Observable.fromArray(array).flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return list; } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableAsyncPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableAsyncPerf.java index 83f2d4c4f4..0ebb2d4458 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableAsyncPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableAsyncPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ public class FlowableFlatMapCompletableAsyncPerf implements Action { Flowable flatMap; @Override - public void run() throws Exception { + public void run() { Blackhole.consumeCPU(work); } diff --git a/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableSyncPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableSyncPerf.java index 34f53a954a..400e0609a8 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableSyncPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/FlowableFlatMapCompletableSyncPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/InputWithIncrementingInteger.java b/src/jmh/java/io/reactivex/rxjava3/core/InputWithIncrementingInteger.java index 24c4e016d3..44d109fb4d 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/InputWithIncrementingInteger.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/InputWithIncrementingInteger.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -43,7 +43,7 @@ public void onNext(Integer t) { } } - final class IncrementingIterable implements Iterable { + static final class IncrementingIterable implements Iterable { final class IncrementingIterator implements Iterator { int i; @@ -77,7 +77,7 @@ public Iterator iterator() { } } - final class IncrementingPublisher implements Publisher { + static final class IncrementingPublisher implements Publisher { final int size; diff --git a/src/jmh/java/io/reactivex/rxjava3/core/JustAsyncPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/JustAsyncPerf.java index d22c1699e5..6906470aea 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/JustAsyncPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/JustAsyncPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -113,71 +113,71 @@ public void subscribeOnFlowable(Blackhole bh) { @Benchmark public void observeOnFlowable(Blackhole bh) { observeOnFlowable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineFlowable(Blackhole bh) { pipelineFlowable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnObservable(Blackhole bh) { subscribeOnObservable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnObservable(Blackhole bh) { observeOnObservable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineObservable(Blackhole bh) { pipelineObservable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnSingle(Blackhole bh) { observeOnSingle.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnSingle(Blackhole bh) { subscribeOnSingle.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineSingle(Blackhole bh) { pipelineSingle.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnCompletable(Blackhole bh) { observeOnCompletable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnCompletable(Blackhole bh) { subscribeOnCompletable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineCompletable(Blackhole bh) { pipelineCompletable.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void observeOnMaybe(Blackhole bh) { observeOnMaybe.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void subscribeOnMaybe(Blackhole bh) { subscribeOnMaybe.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } @Benchmark public void pipelineMaybe(Blackhole bh) { pipelineMaybe.subscribeWith(new PerfAsyncConsumer(bh)).await(1); - }; + } } diff --git a/src/jmh/java/io/reactivex/rxjava3/core/LatchedSingleObserver.java b/src/jmh/java/io/reactivex/rxjava3/core/LatchedSingleObserver.java index 1b908ac6b2..6bcdba465e 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/LatchedSingleObserver.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/LatchedSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/MemoryPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/MemoryPerf.java index 7679ec5346..7cd45de74e 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/MemoryPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/MemoryPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -143,31 +143,31 @@ public static void main(String[] args) throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.just(1); } }, "just", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10); } }, "range", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.empty(); } }, "empty", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.fromCallable(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return 1; } }); @@ -176,38 +176,38 @@ public Object call() throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return new MyRx2Observer(); } }, "consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { - return new io.reactivex.rxjava3.observers.TestObserver(); + public Object call() { + return new io.reactivex.rxjava3.observers.TestObserver<>(); } }, "test-consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.just(1).subscribeWith(new MyRx2Observer()); } }, "just+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10).subscribeWith(new MyRx2Observer()); } }, "range+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10).map(new Function() { @Override - public Object apply(Integer v) throws Exception { + public Object apply(Integer v) { return v; } }).subscribeWith(new MyRx2Observer()); @@ -216,15 +216,15 @@ public Object apply(Integer v) throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10).map(new Function() { @Override - public Object apply(Integer v) throws Exception { + public Object apply(Integer v) { return v; } }).filter(new Predicate() { @Override - public boolean test(Object v) throws Exception { + public boolean test(Object v) { return true; } }).subscribeWith(new MyRx2Observer()); @@ -233,91 +233,91 @@ public boolean test(Object v) throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10).subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).subscribeWith(new MyRx2Observer()); } }, "range+subscribeOn+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10).observeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).subscribeWith(new MyRx2Observer()); } }, "range+observeOn+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Observable.range(1, 10).subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).observeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).subscribeWith(new MyRx2Observer()); } }, "range+subscribeOn+observeOn+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.AsyncSubject.create(); } }, "Async", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.PublishSubject.create(); } }, "Publish", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.ReplaySubject.create(); } }, "Replay", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.BehaviorSubject.create(); } }, "Behavior", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.UnicastSubject.create(); } }, "Unicast", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.AsyncSubject.create().subscribeWith(new MyRx2Observer()); } }, "Async+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.PublishSubject.create().subscribeWith(new MyRx2Observer()); } }, "Publish+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.ReplaySubject.create().subscribeWith(new MyRx2Observer()); } }, "Replay+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.BehaviorSubject.create().subscribeWith(new MyRx2Observer()); } }, "Behavior+consumer", "Rx2Observable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.subjects.UnicastSubject.create().subscribeWith(new MyRx2Observer()); } }, "Unicast+consumer", "Rx2Observable"); @@ -326,38 +326,38 @@ public Object call() throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.just(1); } }, "just", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10); } }, "range", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.empty(); } }, "empty", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.empty(); } }, "empty", "Rx2Flowable", 10000000); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.fromCallable(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return 1; } }); @@ -366,38 +366,38 @@ public Object call() throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return new MyRx2Subscriber(); } }, "consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { - return new io.reactivex.rxjava3.observers.TestObserver(); + public Object call() { + return new io.reactivex.rxjava3.observers.TestObserver<>(); } }, "test-consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.just(1).subscribeWith(new MyRx2Subscriber()); } }, "just+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10).subscribeWith(new MyRx2Subscriber()); } }, "range+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10).map(new Function() { @Override - public Object apply(Integer v) throws Exception { + public Object apply(Integer v) { return v; } }).subscribeWith(new MyRx2Subscriber()); @@ -406,15 +406,15 @@ public Object apply(Integer v) throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10).map(new Function() { @Override - public Object apply(Integer v) throws Exception { + public Object apply(Integer v) { return v; } }).filter(new Predicate() { @Override - public boolean test(Object v) throws Exception { + public boolean test(Object v) { return true; } }).subscribeWith(new MyRx2Subscriber()); @@ -423,91 +423,91 @@ public boolean test(Object v) throws Exception { checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10).subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).subscribeWith(new MyRx2Subscriber()); } }, "range+subscribeOn+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10).observeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).subscribeWith(new MyRx2Subscriber()); } }, "range+observeOn+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.core.Flowable.range(1, 10).subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).observeOn(io.reactivex.rxjava3.schedulers.Schedulers.computation()).subscribeWith(new MyRx2Subscriber()); } }, "range+subscribeOn+observeOn+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.AsyncProcessor.create(); } }, "Async", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.PublishProcessor.create(); } }, "Publish", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.ReplayProcessor.create(); } }, "Replay", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.BehaviorProcessor.create(); } }, "Behavior", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.UnicastProcessor.create(); } }, "Unicast", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.AsyncProcessor.create().subscribeWith(new MyRx2Subscriber()); } }, "Async+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.PublishProcessor.create().subscribeWith(new MyRx2Subscriber()); } }, "Publish+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.ReplayProcessor.create().subscribeWith(new MyRx2Subscriber()); } }, "Replay+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.BehaviorProcessor.create().subscribeWith(new MyRx2Subscriber()); } }, "Behavior+consumer", "Rx2Flowable"); checkMemory(new Callable() { @Override - public Object call() throws Exception { + public Object call() { return io.reactivex.rxjava3.processors.UnicastProcessor.create().subscribeWith(new MyRx2Subscriber()); } }, "Unicast+consumer", "Rx2Flowable"); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/ObservableFlatMapPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/ObservableFlatMapPerf.java index d0d75e78d4..65145b07b4 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/ObservableFlatMapPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/ObservableFlatMapPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/OperatorFlatMapPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/OperatorFlatMapPerf.java index 621894bd74..777fc9e1a6 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/OperatorFlatMapPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/OperatorFlatMapPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public int getSize() { } @Benchmark - public void flatMapIntPassthruSync(Input input) throws InterruptedException { + public void flatMapIntPassthruSync(Input input) { input.flowable.flatMap(new Function>() { @Override public Publisher apply(Integer v) { @@ -66,7 +66,7 @@ public Publisher apply(Integer i) { } @Benchmark - public void flatMapTwoNestedSync(final Input input) throws InterruptedException { + public void flatMapTwoNestedSync(final Input input) { Flowable.range(1, 2).flatMap(new Function>() { @Override public Publisher apply(Integer i) { diff --git a/src/jmh/java/io/reactivex/rxjava3/core/OperatorMergePerf.java b/src/jmh/java/io/reactivex/rxjava3/core/OperatorMergePerf.java index 7d68316c83..24605fc02a 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/OperatorMergePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/OperatorMergePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -134,7 +134,7 @@ public static class InputForMergeN { @Setup public void setup(final Blackhole bh) { this.bh = bh; - observables = new ArrayList>(); + observables = new ArrayList<>(); for (int i = 0; i < size; i++) { observables.add(Flowable.just(i)); } diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PerfAsyncConsumer.java b/src/jmh/java/io/reactivex/rxjava3/core/PerfAsyncConsumer.java index 7c222fddab..ccaaf75573 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PerfAsyncConsumer.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PerfAsyncConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PerfBoundedSubscriber.java b/src/jmh/java/io/reactivex/rxjava3/core/PerfBoundedSubscriber.java index 8ec19a833a..68083976be 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PerfBoundedSubscriber.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PerfBoundedSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PerfConsumer.java b/src/jmh/java/io/reactivex/rxjava3/core/PerfConsumer.java index 6025438218..60f93f089a 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PerfConsumer.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PerfConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PerfInteropConsumer.java b/src/jmh/java/io/reactivex/rxjava3/core/PerfInteropConsumer.java index 6dfbac7a31..fc7f0b04b0 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PerfInteropConsumer.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PerfInteropConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PerfObserver.java b/src/jmh/java/io/reactivex/rxjava3/core/PerfObserver.java index 5f09b97ffe..a62a1e68f6 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PerfObserver.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PerfObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PerfSubscriber.java b/src/jmh/java/io/reactivex/rxjava3/core/PerfSubscriber.java index e4c52b5040..8c0f30cffa 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PerfSubscriber.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PerfSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,7 +20,7 @@ public class PerfSubscriber implements FlowableSubscriber { - public CountDownLatch latch = new CountDownLatch(1); + public final CountDownLatch latch = new CountDownLatch(1); private final Blackhole bh; public PerfSubscriber(Blackhole bh) { diff --git a/src/jmh/java/io/reactivex/rxjava3/core/PublishProcessorPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/PublishProcessorPerf.java index c64e1f794d..1278bfd641 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/PublishProcessorPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/PublishProcessorPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/RangePerf.java b/src/jmh/java/io/reactivex/rxjava3/core/RangePerf.java index c9723b086d..0773cd4f72 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/RangePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/RangePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/ReducePerf.java b/src/jmh/java/io/reactivex/rxjava3/core/ReducePerf.java index 0282aa31e5..3d2bb476c8 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/ReducePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/ReducePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public class ReducePerf implements BiFunction { Maybe flowMaybe; @Override - public Integer apply(Integer t1, Integer t2) throws Exception { + public Integer apply(Integer t1, Integer t2) { return t1 + t2; } diff --git a/src/jmh/java/io/reactivex/rxjava3/core/RxVsStreamPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/RxVsStreamPerf.java index 74c8952c7a..8861a6cdf5 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/RxVsStreamPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/RxVsStreamPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/StrictPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/StrictPerf.java index 59d17f4d60..23315ef617 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/StrictPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/StrictPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/jmh/java/io/reactivex/rxjava3/core/TakeUntilPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/TakeUntilPerf.java index 71161b32d3..a6a3949605 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/TakeUntilPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/TakeUntilPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public class TakeUntilPerf implements Consumer { Observable observable; @Override - public void accept(Integer t) throws Exception { + public void accept(Integer t) { items++; } @@ -48,7 +48,7 @@ public void setup() { flowable = Flowable.range(1, 1000 * 1000).takeUntil(Flowable.fromCallable(new Callable() { @Override - public Object call() throws Exception { + public Object call() { int c = count; while (items < c) { } return 1; @@ -57,7 +57,7 @@ public Object call() throws Exception { observable = Observable.range(1, 1000 * 1000).takeUntil(Observable.fromCallable(new Callable() { @Override - public Object call() throws Exception { + public Object call() { int c = count; while (items < c) { } return 1; @@ -71,7 +71,7 @@ public void flowable() { flowable.subscribe(this, Functions.emptyConsumer(), new Action() { @Override - public void run() throws Exception { + public void run() { cdl.countDown(); } }); @@ -85,7 +85,7 @@ public void observable() { observable.subscribe(this, Functions.emptyConsumer(), new Action() { @Override - public void run() throws Exception { + public void run() { cdl.countDown(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/ToFlowablePerf.java b/src/jmh/java/io/reactivex/rxjava3/core/ToFlowablePerf.java index 28b1d050fc..7b7f0dc36d 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/ToFlowablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/ToFlowablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,7 +48,7 @@ public void setup() { final BiFunction second = new BiFunction() { @Override - public Integer apply(Integer a, Integer b) throws Exception { + public Integer apply(Integer a, Integer b) { return b; } }; @@ -57,7 +57,7 @@ public Integer apply(Integer a, Integer b) throws Exception { flowableInner = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Flowable.range(1, 50).reduce(second).toFlowable(); } }); @@ -68,7 +68,7 @@ public Publisher apply(Integer v) throws Exception { observableInner = sourceObs.concatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Observable.range(1, 50).reduce(second).toObservable(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/core/XMapYPerf.java b/src/jmh/java/io/reactivex/rxjava3/core/XMapYPerf.java index 582d9da8dc..b95d76ffe4 100644 --- a/src/jmh/java/io/reactivex/rxjava3/core/XMapYPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/core/XMapYPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -103,84 +103,84 @@ public void setup() { flowFlatMapFlowable1 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowFlatMapFlowable0 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); flowFlatMapSingle1 = fsource.flatMapSingle(new Function>() { @Override - public SingleSource apply(Integer v) throws Exception { + public SingleSource apply(Integer v) { return Single.just(v); } }); flowFlatMapMaybe1 = fsource.flatMapMaybe(new Function>() { @Override - public MaybeSource apply(Integer v) throws Exception { + public MaybeSource apply(Integer v) { return Maybe.just(v); } }); flowFlatMapMaybe0 = fsource.flatMapMaybe(new Function>() { @Override - public MaybeSource apply(Integer v) throws Exception { + public MaybeSource apply(Integer v) { return Maybe.empty(); } }); flowFlatMapCompletable0 = fsource.flatMapCompletable(new Function() { @Override - public CompletableSource apply(Integer v) throws Exception { + public CompletableSource apply(Integer v) { return Completable.complete(); } }); flowFlatMapIterable1 = fsource.flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return Collections.singletonList(v); } }); flowFlatMapIterable0 = fsource.flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { - return Collections.emptyList(); + public Iterable apply(Integer v) { + return Collections.emptyList(); } }); flowFlatMapSingle1 = fsource.flatMapSingle(new Function>() { @Override - public SingleSource apply(Integer v) throws Exception { + public SingleSource apply(Integer v) { return Single.just(v); } }); flowFlatMapMaybe1 = fsource.flatMapMaybe(new Function>() { @Override - public MaybeSource apply(Integer v) throws Exception { + public MaybeSource apply(Integer v) { return Maybe.just(v); } }); flowFlatMapMaybe0 = fsource.flatMapMaybe(new Function>() { @Override - public MaybeSource apply(Integer v) throws Exception { + public MaybeSource apply(Integer v) { return Maybe.empty(); } }); flowFlatMapCompletable0 = fsource.flatMapCompletable(new Function() { @Override - public CompletableSource apply(Integer v) throws Exception { + public CompletableSource apply(Integer v) { return Completable.complete(); } }); @@ -189,43 +189,43 @@ public CompletableSource apply(Integer v) throws Exception { flowFlatMapSingleAsFlow1 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Single.just(v).toFlowable(); } }); flowFlatMapMaybeAsFlow1 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Maybe.just(v).toFlowable(); } }); flowFlatMapMaybeAsFlow0 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Maybe.empty().toFlowable(); } }); flowFlatMapCompletableAsFlow0 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { - return Completable.complete().toFlowable(); + public Publisher apply(Integer v) { + return Completable.complete().toFlowable(); } }); flowFlatMapIterableAsFlow1 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Flowable.fromIterable(Collections.singletonList(v)); } }); flowFlatMapIterableAsFlow0 = fsource.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { - return Flowable.fromIterable(Collections.emptyList()); + public Publisher apply(Integer v) { + return Flowable.fromIterable(Collections.emptyList()); } }); @@ -235,57 +235,57 @@ public Publisher apply(Integer v) throws Exception { obsFlatMapObservable1 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); obsFlatMapObservable0 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); obsFlatMapSingle1 = osource.flatMapSingle(new Function>() { @Override - public SingleSource apply(Integer v) throws Exception { + public SingleSource apply(Integer v) { return Single.just(v); } }); obsFlatMapMaybe1 = osource.flatMapMaybe(new Function>() { @Override - public MaybeSource apply(Integer v) throws Exception { + public MaybeSource apply(Integer v) { return Maybe.just(v); } }); obsFlatMapMaybe0 = osource.flatMapMaybe(new Function>() { @Override - public MaybeSource apply(Integer v) throws Exception { + public MaybeSource apply(Integer v) { return Maybe.empty(); } }); obsFlatMapCompletable0 = osource.flatMapCompletable(new Function() { @Override - public CompletableSource apply(Integer v) throws Exception { + public CompletableSource apply(Integer v) { return Completable.complete(); } }); obsFlatMapIterable1 = osource.flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { + public Iterable apply(Integer v) { return Collections.singletonList(v); } }); obsFlatMapIterable0 = osource.flatMapIterable(new Function>() { @Override - public Iterable apply(Integer v) throws Exception { - return Collections.emptyList(); + public Iterable apply(Integer v) { + return Collections.emptyList(); } }); @@ -293,43 +293,43 @@ public Iterable apply(Integer v) throws Exception { obsFlatMapSingleAsObs1 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Single.just(v).toObservable(); } }); obsFlatMapMaybeAsObs1 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Maybe.just(v).toObservable(); } }); obsFlatMapMaybeAsObs0 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Maybe.empty().toObservable(); } }); obsFlatMapCompletableAsObs0 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { - return Completable.complete().toObservable(); + public Observable apply(Integer v) { + return Completable.complete().toObservable(); } }); obsFlatMapIterableAsObs1 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { + public Observable apply(Integer v) { return Observable.fromIterable(Collections.singletonList(v)); } }); obsFlatMapIterableAsObs0 = osource.flatMap(new Function>() { @Override - public Observable apply(Integer v) throws Exception { - return Observable.fromIterable(Collections.emptyList()); + public Observable apply(Integer v) { + return Observable.fromIterable(Collections.emptyList()); } }); } diff --git a/src/jmh/java/io/reactivex/rxjava3/parallel/ParallelPerf.java b/src/jmh/java/io/reactivex/rxjava3/parallel/ParallelPerf.java index 31d0ecc8c5..c6fd16ffb6 100644 --- a/src/jmh/java/io/reactivex/rxjava3/parallel/ParallelPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/parallel/ParallelPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -49,7 +49,7 @@ public class ParallelPerf implements Function { Flowable parallel; @Override - public Integer apply(Integer t) throws Exception { + public Integer apply(Integer t) { Blackhole.consumeCPU(compute); return t; } @@ -66,7 +66,7 @@ public void setup() { flatMap = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v).subscribeOn(Schedulers.computation()) .map(ParallelPerf.this); } @@ -75,13 +75,13 @@ public Publisher apply(Integer v) throws Exception { groupBy = source.groupBy(new Function() { int i; @Override - public Integer apply(Integer v) throws Exception { + public Integer apply(Integer v) { return (i++) % cpu; } }) .flatMap(new Function, Publisher>() { @Override - public Publisher apply(GroupedFlowable g) throws Exception { + public Publisher apply(GroupedFlowable g) { return g.observeOn(Schedulers.computation()).map(ParallelPerf.this); } }); @@ -109,4 +109,4 @@ public void groupBy(Blackhole bh) { public void parallel(Blackhole bh) { subscribe(parallel, bh); } -} \ No newline at end of file +} diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapCompletablePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapCompletablePerf.java index cb58173206..f1124f7a81 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapCompletablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapCompletablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); flowableConvert = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Completable.complete().toFlowable(); } }); flowableDedicated = source.concatMapCompletable(new Function() { @Override - public Completable apply(Integer v) - throws Exception { + public Completable apply(Integer v) { return Completable.complete(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybeEmptyPerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybeEmptyPerf.java index 1aff189e56..7eabfcfbde 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybeEmptyPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybeEmptyPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); concatMapToFlowableEmpty = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Maybe.empty().toFlowable(); } }); flowableDedicated = source.concatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.empty(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybePerf.java index 38ca515171..4310ea2e95 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapMaybePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowableConvert = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Maybe.just(v).toFlowable(); } }); - flowableDedicated = source.concatMapMaybe(new Function>() { + flowableDedicated = source.concatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapSinglePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapSinglePerf.java index 04148d7a47..cbeac5ada9 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapSinglePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableConcatMapSinglePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowableConvert = source.concatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Single.just(v).toFlowable(); } }); flowableDedicated = source.concatMapSingle(new Function>() { @Override - public Single apply(Integer v) - throws Exception { + public Single apply(Integer v) { return Single.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapCompletablePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapCompletablePerf.java index e605629a8d..b46725986f 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapCompletablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapCompletablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); flowableConvert = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Completable.complete().toFlowable(); } }); flowableDedicated = source.flatMapCompletable(new Function() { @Override - public Completable apply(Integer v) - throws Exception { + public Completable apply(Integer v) { return Completable.complete(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybeEmptyPerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybeEmptyPerf.java index 23bd80fe6b..699f76c074 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybeEmptyPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybeEmptyPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); flowableConvert = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Maybe.empty().toFlowable(); } }); - flowableDedicated = source.flatMapMaybe(new Function>() { + flowableDedicated = source.flatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.empty(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybePerf.java index bbbc96fc03..f81ed10ec3 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapMaybePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowableConvert = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Maybe.just(v).toFlowable(); } }); - flowableDedicated = source.flatMapMaybe(new Function>() { + flowableDedicated = source.flatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapSinglePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapSinglePerf.java index 8407a60e6a..5a92bf20ff 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapSinglePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableFlatMapSinglePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowableConvert = source.flatMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Single.just(v).toFlowable(); } }); - flowableDedicated = source.flatMapSingle(new Function>() { + flowableDedicated = source.flatMapSingle(new Function>() { @Override - public Single apply(Integer v) - throws Exception { + public Single apply(Integer v) { return Single.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapCompletablePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapCompletablePerf.java index 25b4e34529..4e419a5d2e 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapCompletablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapCompletablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); flowableConvert = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Completable.complete().toFlowable(); } }); flowableDedicated = source.switchMapCompletable(new Function() { @Override - public Completable apply(Integer v) - throws Exception { + public Completable apply(Integer v) { return Completable.complete(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybeEmptyPerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybeEmptyPerf.java index ff6d5999ba..46ce694f6d 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybeEmptyPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybeEmptyPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.empty(); } }); flowableConvert = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Maybe.empty().toFlowable(); } }); - flowableDedicated = source.switchMapMaybe(new Function>() { + flowableDedicated = source.switchMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.empty(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybePerf.java index 87071d1108..e96bbc3919 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapMaybePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowableConvert = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Maybe.just(v).toFlowable(); } }); - flowableDedicated = source.switchMapMaybe(new Function>() { + flowableDedicated = source.switchMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapSinglePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapSinglePerf.java index a633856881..ef06ebfa66 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapSinglePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/FlowableSwitchMapSinglePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,24 +48,21 @@ public void setup() { flowablePlain = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Flowable.just(v); } }); flowableConvert = source.switchMap(new Function>() { @Override - public Publisher apply(Integer v) - throws Exception { + public Publisher apply(Integer v) { return Single.just(v).toFlowable(); } }); - flowableDedicated = source.switchMapSingle(new Function>() { + flowableDedicated = source.switchMapSingle(new Function>() { @Override - public Single apply(Integer v) - throws Exception { + public Single apply(Integer v) { return Single.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapCompletablePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapCompletablePerf.java index 5b2b9247fc..2229eed77a 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapCompletablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapCompletablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.concatMap(new Function>() { + observablePlain = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); - observableConvert = source.concatMap(new Function>() { + observableConvert = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Completable.complete().toObservable(); } }); observableDedicated = source.concatMapCompletable(new Function() { @Override - public Completable apply(Integer v) - throws Exception { + public Completable apply(Integer v) { return Completable.complete(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybeEmptyPerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybeEmptyPerf.java index 0ffbfcd321..cfde5183e5 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybeEmptyPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybeEmptyPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.concatMap(new Function>() { + observablePlain = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); - concatMapToObservableEmpty = source.concatMap(new Function>() { + concatMapToObservableEmpty = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Maybe.empty().toObservable(); } }); - observableDedicated = source.concatMapMaybe(new Function>() { + observableDedicated = source.concatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.empty(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybePerf.java index 2ec317234b..75e7506724 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapMaybePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.concatMap(new Function>() { + observablePlain = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); - observableConvert = source.concatMap(new Function>() { + observableConvert = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Maybe.just(v).toObservable(); } }); - observableDedicated = source.concatMapMaybe(new Function>() { + observableDedicated = source.concatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapSinglePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapSinglePerf.java index e67138c816..4227791222 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapSinglePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableConcatMapSinglePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.concatMap(new Function>() { + observablePlain = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); - observableConvert = source.concatMap(new Function>() { + observableConvert = source.concatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Single.just(v).toObservable(); } }); - observableDedicated = source.concatMapSingle(new Function>() { + observableDedicated = source.concatMapSingle(new Function>() { @Override - public Single apply(Integer v) - throws Exception { + public Single apply(Integer v) { return Single.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapCompletablePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapCompletablePerf.java index 6fc1ed9b07..6a916a68f1 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapCompletablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapCompletablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.flatMap(new Function>() { + observablePlain = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); - observableConvert = source.flatMap(new Function>() { + observableConvert = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Completable.complete().toObservable(); } }); observableDedicated = source.flatMapCompletable(new Function() { @Override - public Completable apply(Integer v) - throws Exception { + public Completable apply(Integer v) { return Completable.complete(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybeEmptyPerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybeEmptyPerf.java index d1b8dea70a..377a8bba93 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybeEmptyPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybeEmptyPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.flatMap(new Function>() { + observablePlain = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); - observableConvert = source.flatMap(new Function>() { + observableConvert = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Maybe.empty().toObservable(); } }); - observableDedicated = source.flatMapMaybe(new Function>() { + observableDedicated = source.flatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.empty(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybePerf.java index 37bf095ce5..248ca98112 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapMaybePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.flatMap(new Function>() { + observablePlain = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); - observableConvert = source.flatMap(new Function>() { + observableConvert = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Maybe.just(v).toObservable(); } }); - observableDedicated = source.flatMapMaybe(new Function>() { + observableDedicated = source.flatMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapSinglePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapSinglePerf.java index 6139e6e080..880da95f5a 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapSinglePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableFlatMapSinglePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.flatMap(new Function>() { + observablePlain = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); - observableConvert = source.flatMap(new Function>() { + observableConvert = source.flatMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Single.just(v).toObservable(); } }); - observableDedicated = source.flatMapSingle(new Function>() { + observableDedicated = source.flatMapSingle(new Function>() { @Override - public Single apply(Integer v) - throws Exception { + public Single apply(Integer v) { return Single.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapCompletablePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapCompletablePerf.java index 7f91a83adf..41964c3dbd 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapCompletablePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapCompletablePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.switchMap(new Function>() { + observablePlain = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); - observableConvert = source.switchMap(new Function>() { + observableConvert = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Completable.complete().toObservable(); } }); observableDedicated = source.switchMapCompletable(new Function() { @Override - public Completable apply(Integer v) - throws Exception { + public Completable apply(Integer v) { return Completable.complete(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybeEmptyPerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybeEmptyPerf.java index 967a244b07..6a4ea5c73b 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybeEmptyPerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybeEmptyPerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.switchMap(new Function>() { + observablePlain = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.empty(); } }); - observableConvert = source.switchMap(new Function>() { + observableConvert = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Maybe.empty().toObservable(); } }); - observableDedicated = source.switchMapMaybe(new Function>() { + observableDedicated = source.switchMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.empty(); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybePerf.java index 123e88b921..f0c3285890 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapMaybePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.switchMap(new Function>() { + observablePlain = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); - observableConvert = source.switchMap(new Function>() { + observableConvert = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Maybe.just(v).toObservable(); } }); - observableDedicated = source.switchMapMaybe(new Function>() { + observableDedicated = source.switchMapMaybe(new Function>() { @Override - public Maybe apply(Integer v) - throws Exception { + public Maybe apply(Integer v) { return Maybe.just(v); } }); diff --git a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapSinglePerf.java b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapSinglePerf.java index abe4ea45b3..087f32c8e3 100644 --- a/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapSinglePerf.java +++ b/src/jmh/java/io/reactivex/rxjava3/xmapz/ObservableSwitchMapSinglePerf.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,26 +45,23 @@ public void setup() { Observable source = Observable.fromArray(sourceArray); - observablePlain = source.switchMap(new Function>() { + observablePlain = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Observable.just(v); } }); - observableConvert = source.switchMap(new Function>() { + observableConvert = source.switchMap(new Function>() { @Override - public Observable apply(Integer v) - throws Exception { + public Observable apply(Integer v) { return Single.just(v).toObservable(); } }); - observableDedicated = source.switchMapSingle(new Function>() { + observableDedicated = source.switchMapSingle(new Function>() { @Override - public Single apply(Integer v) - throws Exception { + public Single apply(Integer v) { return Single.just(v); } }); diff --git a/src/main/java/io/reactivex/rxjava3/annotations/BackpressureKind.java b/src/main/java/io/reactivex/rxjava3/annotations/BackpressureKind.java index aa20e5f78e..3b979a045d 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/BackpressureKind.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/BackpressureKind.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,13 +32,13 @@ public enum BackpressureKind { */ SPECIAL, /** - * The operator requests Long.MAX_VALUE from upstream but respects the backpressure + * The operator requests {@link Long#MAX_VALUE} from upstream but respects the backpressure * of the downstream. */ UNBOUNDED_IN, /** - * The operator will emit a MissingBackpressureException if the downstream didn't request - * enough or in time. + * The operator will emit a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException MissingBackpressureException} + * if the downstream didn't request enough or in time. */ ERROR, /** diff --git a/src/main/java/io/reactivex/rxjava3/annotations/BackpressureSupport.java b/src/main/java/io/reactivex/rxjava3/annotations/BackpressureSupport.java index 9deafa22f5..b73a477fec 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/BackpressureSupport.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/BackpressureSupport.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/annotations/Beta.java b/src/main/java/io/reactivex/rxjava3/annotations/Beta.java index 123842b773..ca75ea0b3b 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/Beta.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/Beta.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/annotations/CheckReturnValue.java b/src/main/java/io/reactivex/rxjava3/annotations/CheckReturnValue.java index 3091d2647f..02c7f2e0d9 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/CheckReturnValue.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/CheckReturnValue.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/annotations/Experimental.java b/src/main/java/io/reactivex/rxjava3/annotations/Experimental.java index 32d60e1442..061361f9b4 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/Experimental.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/Experimental.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/annotations/NonNull.java b/src/main/java/io/reactivex/rxjava3/annotations/NonNull.java index 06f1d4ee8b..4495092fc7 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/NonNull.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/NonNull.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,10 @@ import java.lang.annotation.*; /** - * Indicates that a field/parameter/variable/return type is never null. + * Indicates that a field/parameter/variable/type parameter/return type is never null. */ @Documented -@Target(value = {FIELD, METHOD, PARAMETER, LOCAL_VARIABLE}) +@Target(value = {FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_PARAMETER, TYPE_USE}) @Retention(value = CLASS) public @interface NonNull { } diff --git a/src/main/java/io/reactivex/rxjava3/annotations/Nullable.java b/src/main/java/io/reactivex/rxjava3/annotations/Nullable.java index c94cef974b..a6af9eb1b5 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/Nullable.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/Nullable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,10 @@ import java.lang.annotation.*; /** - * Indicates that a field/parameter/variable/return type may be null. + * Indicates that a field/parameter/variable/type parameter/return type may be null. */ @Documented -@Target(value = {FIELD, METHOD, PARAMETER, LOCAL_VARIABLE}) +@Target(value = {FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_PARAMETER, TYPE_USE}) @Retention(value = CLASS) public @interface Nullable { } diff --git a/src/main/java/io/reactivex/rxjava3/annotations/SchedulerSupport.java b/src/main/java/io/reactivex/rxjava3/annotations/SchedulerSupport.java index 53b395f06b..0132b6a33d 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/SchedulerSupport.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/SchedulerSupport.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/annotations/package-info.java b/src/main/java/io/reactivex/rxjava3/annotations/package-info.java index 1f84d214c7..a4daca2a32 100644 --- a/src/main/java/io/reactivex/rxjava3/annotations/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/annotations/package-info.java @@ -1,20 +1,19 @@ -/** +/* * 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. */ /** - * Annotations for indicating experimental and beta operators, classes, methods, types or fields. + * Annotations for indicating operator behavior, API stability + * ({@link io.reactivex.rxjava3.annotations.Experimental @Experimental} and {@link io.reactivex.rxjava3.annotations.Beta @Beta}) and + * nullability indicators ({@link io.reactivex.rxjava3.annotations.Nullable Nullable} and {@link io.reactivex.rxjava3.annotations.NonNull NonNull}). */ package io.reactivex.rxjava3.annotations; diff --git a/src/main/java/io/reactivex/rxjava3/core/BackpressureOverflowStrategy.java b/src/main/java/io/reactivex/rxjava3/core/BackpressureOverflowStrategy.java index 5ee8ecca1d..144dbb6807 100644 --- a/src/main/java/io/reactivex/rxjava3/core/BackpressureOverflowStrategy.java +++ b/src/main/java/io/reactivex/rxjava3/core/BackpressureOverflowStrategy.java @@ -1,25 +1,26 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.core; /** * Options to deal with buffer overflow when using onBackpressureBuffer. */ public enum BackpressureOverflowStrategy { - /** Signal a MissingBackpressureException and terminate the sequence. */ + /** + * Signal a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException MissingBackpressureException} + * and terminate the sequence. + */ ERROR, /** Drop the oldest value from the buffer. */ DROP_OLDEST, diff --git a/src/main/java/io/reactivex/rxjava3/core/BackpressureStrategy.java b/src/main/java/io/reactivex/rxjava3/core/BackpressureStrategy.java index c0831d7759..f2bf8d01ae 100644 --- a/src/main/java/io/reactivex/rxjava3/core/BackpressureStrategy.java +++ b/src/main/java/io/reactivex/rxjava3/core/BackpressureStrategy.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,25 +18,26 @@ */ public enum BackpressureStrategy { /** - * OnNext events are written without any buffering or dropping. + * The {@code onNext} events are written without any buffering or dropping. * Downstream has to deal with any overflow. *

Useful when one applies one of the custom-parameter onBackpressureXXX operators. */ MISSING, /** - * Signals a MissingBackpressureException in case the downstream can't keep up. + * Signals a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException MissingBackpressureException} + * in case the downstream can't keep up. */ ERROR, /** - * Buffers all onNext values until the downstream consumes it. + * Buffers all {@code onNext} values until the downstream consumes it. */ BUFFER, /** - * Drops the most recent onNext value if the downstream can't keep up. + * Drops the most recent {@code onNext} value if the downstream can't keep up. */ DROP, /** - * Keeps only the latest onNext value, overwriting any previous value if the + * Keeps only the latest {@code onNext} value, overwriting any previous value if the * downstream can't keep up. */ LATEST diff --git a/src/main/java/io/reactivex/rxjava3/core/Completable.java b/src/main/java/io/reactivex/rxjava3/core/Completable.java index 06183c66db..e31a44c32c 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Completable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Completable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,18 +10,21 @@ * 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 io.reactivex.rxjava3.core; +import java.util.*; import java.util.concurrent.*; -import org.reactivestreams.Publisher; +import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.jdk8.*; import io.reactivex.rxjava3.internal.observers.*; import io.reactivex.rxjava3.internal.operators.completable.*; import io.reactivex.rxjava3.internal.operators.maybe.*; @@ -48,8 +51,8 @@ *

* Note that as with the {@code Observable} protocol, {@code onError} and {@code onComplete} are mutually exclusive events. *

- * Like {@link Observable}, a running {@code Completable} can be stopped through the {@link Disposable} instance - * provided to consumers through {@link SingleObserver#onSubscribe}. + * Like {@code Observable}, a running {@code Completable} can be stopped through the {@link Disposable} instance + * provided to consumers through {@link CompletableObserver#onSubscribe}. *

* Like an {@code Observable}, a {@code Completable} is lazy, can be either "hot" or "cold", synchronous or * asynchronous. {@code Completable} instances returned by the methods of this class are cold @@ -60,7 +63,7 @@ *

* *

- * See {@link Flowable} or {@link Observable} for the + * See {@link Flowable} or {@code Observable} for the * implementation of the Reactive Pattern for a stream or vector of values. *

* Example: @@ -103,24 +106,25 @@ */ public abstract class Completable implements CompletableSource { /** - * Returns a Completable which terminates as soon as one of the source Completables - * terminates (normally or with an error) and disposes all other Completables. + * Returns a {@code Completable} which terminates as soon as one of the source {@code Completable}s + * terminates (normally or with an error) and disposes all other {@code Completable}s. *

* *

*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the array of source Completables. A subscription to each source will + * @param sources the array of source {@code Completable}s. A subscription to each source will * occur in the same order as in this array. - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable ambArray(final CompletableSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static Completable ambArray(@NonNull CompletableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return complete(); } @@ -132,37 +136,37 @@ public static Completable ambArray(final CompletableSource... sources) { } /** - * Returns a Completable which terminates as soon as one of the source Completables - * terminates (normally or with an error) and disposes all other Completables. + * Returns a {@code Completable} which terminates as soon as one of the source {@code Completable}s in the {@link Iterable} sequence + * terminates (normally or with an error) and disposes all other {@code Completable}s. *

* *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the array of source Completables. A subscription to each source will - * occur in the same order as in this Iterable. - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the {@code Iterable} of source {@code Completable}s. A subscription to each source will + * occur in the same order as in this {@code Iterable}. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable amb(final Iterable sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static Completable amb(@NonNull Iterable<@NonNull ? extends CompletableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return RxJavaPlugins.onAssembly(new CompletableAmb(null, sources)); } /** - * Returns a Completable instance that completes immediately when subscribed to. + * Returns a {@code Completable} instance that completes immediately when subscribed to. *

* *

*
Scheduler:
*
{@code complete} does not operate by default on a particular {@link Scheduler}.
*
- * @return a Completable instance that completes immediately + * @return the shared {@code Completable} instance */ @CheckReturnValue @NonNull @@ -172,22 +176,23 @@ public static Completable complete() { } /** - * Returns a Completable which completes only when all sources complete, one after another. + * Returns a {@code Completable} which completes only when all sources complete, one after another. *

- * + * *

*
Scheduler:
*
{@code concatArray} does not operate by default on a particular {@link Scheduler}.
*
* @param sources the sources to concatenate - * @return the Completable instance which completes only when all sources complete - * @throws NullPointerException if sources is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable concatArray(CompletableSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static Completable concatArray(@NonNull CompletableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return complete(); } else @@ -198,7 +203,28 @@ public static Completable concatArray(CompletableSource... sources) { } /** - * Returns a Completable which completes only when all sources complete, one after another. + * Returns a {@code Completable} which completes only when all sources complete, one after another. + *

+ * + *

+ *
Scheduler:
+ *
{@code concatArrayDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param sources the sources to concatenate + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @SafeVarargs + public static Completable concatArrayDelayError(@NonNull CompletableSource... sources) { + return Flowable.fromArray(sources).concatMapCompletableDelayError(Functions.identity(), true, 2); + } + + /** + * Returns a {@code Completable} which completes only when all sources complete, one after another. *

* *

@@ -206,68 +232,140 @@ public static Completable concatArray(CompletableSource... sources) { *
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param sources the sources to concatenate - * @return the Completable instance which completes only when all sources complete - * @throws NullPointerException if sources is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable concat(Iterable sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static Completable concat(@NonNull Iterable<@NonNull ? extends CompletableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return RxJavaPlugins.onAssembly(new CompletableConcatIterable(sources)); } /** - * Returns a Completable which completes only when all sources complete, one after another. + * Returns a {@code Completable} which completes only when all sources complete, one after another. *

- * + * *

*
Backpressure:
*
The returned {@code Completable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well.
+ * and expects the other {@link Publisher} to honor it as well. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param sources the sources to concatenate - * @return the Completable instance which completes only when all sources complete - * @throws NullPointerException if sources is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Completable concat(Publisher sources) { + @NonNull + public static Completable concat(@NonNull Publisher<@NonNull ? extends CompletableSource> sources) { return concat(sources, 2); } /** - * Returns a Completable which completes only when all sources complete, one after another. + * Returns a {@code Completable} which completes only when all sources complete, one after another. *

- * + * *

*
Backpressure:
*
The returned {@code Completable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well.
+ * and expects the other {@link Publisher} to honor it as well. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param sources the sources to concatenate * @param prefetch the number of sources to prefetch from the sources - * @return the Completable instance which completes only when all sources complete - * @throws NullPointerException if sources is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Completable concat(Publisher sources, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static Completable concat(@NonNull Publisher<@NonNull ? extends CompletableSource> sources, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); return RxJavaPlugins.onAssembly(new CompletableConcat(sources, prefetch)); } /** - * Provides an API (via a cold Completable) that bridges the reactive world with the callback-style world. + * Returns a {@code Completable} which completes only when all sources complete, one after another. + *

+ * + *

+ *
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param sources the sources to concatenate + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static Completable concatDelayError(@NonNull Iterable<@NonNull ? extends CompletableSource> sources) { + return Flowable.fromIterable(sources).concatMapCompletableDelayError(Functions.identity()); + } + + /** + * Returns a {@code Completable} which completes only when all sources complete, one after another. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Completable} honors the backpressure of the downstream consumer + * and expects the other {@link Publisher} to honor it as well.
+ *
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param sources the sources to concatenate + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + @NonNull + public static Completable concatDelayError(@NonNull Publisher<@NonNull ? extends CompletableSource> sources) { + return concatDelayError(sources, 2); + } + + /** + * Returns a {@code Completable} which completes only when all sources complete, one after another. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Completable} honors the backpressure of the downstream consumer + * and expects the other {@link Publisher} to honor it as well.
+ *
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param sources the sources to concatenate + * @param prefetch the number of sources to prefetch from the sources + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public static Completable concatDelayError(@NonNull Publisher<@NonNull ? extends CompletableSource> sources, int prefetch) { + return Flowable.fromPublisher(sources).concatMapCompletableDelayError(Functions.identity(), true, prefetch); + } + + /** + * Provides an API (via a cold {@code Completable}) that bridges the reactive world with the callback-style world. *

* *

@@ -296,27 +394,51 @@ public static Completable concat(Publisher sources, * Whenever a {@link CompletableObserver} subscribes to the returned {@code Completable}, the provided * {@link CompletableOnSubscribe} callback is invoked with a fresh instance of a {@link CompletableEmitter} * that will interact only with that specific {@code CompletableObserver}. If this {@code CompletableObserver} - * disposes the flow (making {@link CompletableEmitter#isDisposed} return true), + * disposes the flow (making {@link CompletableEmitter#isDisposed} return {@code true}), * other observers subscribed to the same returned {@code Completable} are not affected. *

*
Scheduler:
*
{@code create} does not operate by default on a particular {@link Scheduler}.
*
- * @param source the emitter that is called when a CompletableObserver subscribes to the returned {@code Completable} - * @return the new Completable instance + * @param source the emitter that is called when a {@code CompletableObserver} subscribes to the returned {@code Completable} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code source} is {@code null} * @see CompletableOnSubscribe * @see Cancellable */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable create(CompletableOnSubscribe source) { - ObjectHelper.requireNonNull(source, "source is null"); + public static Completable create(@NonNull CompletableOnSubscribe source) { + Objects.requireNonNull(source, "source is null"); return RxJavaPlugins.onAssembly(new CompletableCreate(source)); } /** - * Constructs a Completable instance by wrapping the given source callback + * Compares two {@link CompletableSource}s and emits {@code true} via a {@link Single} if both complete. + *

+ * + *

+ *
Scheduler:
+ *
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param source1 the first {@code CompletableSource} instance + * @param source2 the second {@code CompletableSource} instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static Single sequenceEqual(@NonNull CompletableSource source1, @NonNull CompletableSource source2) { // NOPMD + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + return mergeArrayDelayError(source1, source2).andThen(Single.just(true)); + } + + /** + * Constructs a {@code Completable} instance by wrapping the given source callback * without any safeguards; you should manage the lifecycle and response * to downstream disposal. *

@@ -325,166 +447,173 @@ public static Completable create(CompletableOnSubscribe source) { *

Scheduler:
*
{@code unsafeCreate} does not operate by default on a particular {@link Scheduler}.
* - * @param source the callback which will receive the CompletableObserver instances - * when the Completable is subscribed to. - * @return the created Completable instance - * @throws NullPointerException if source is null + * @param onSubscribe the callback which will receive the {@link CompletableObserver} instances + * when the {@code Completable} is subscribed to. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} + * @throws IllegalArgumentException if {@code source} is a {@code Completable} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable unsafeCreate(CompletableSource source) { - ObjectHelper.requireNonNull(source, "source is null"); - if (source instanceof Completable) { + public static Completable unsafeCreate(@NonNull CompletableSource onSubscribe) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + if (onSubscribe instanceof Completable) { throw new IllegalArgumentException("Use of unsafeCreate(Completable)!"); } - return RxJavaPlugins.onAssembly(new CompletableFromUnsafeSource(source)); + return RxJavaPlugins.onAssembly(new CompletableFromUnsafeSource(onSubscribe)); } /** - * Defers the subscription to a Completable instance returned by a supplier. + * Defers the subscription to a {@code Completable} instance returned by a supplier. *

* *

*
Scheduler:
*
{@code defer} does not operate by default on a particular {@link Scheduler}.
*
- * @param completableSupplier the supplier that returns the Completable that will be subscribed to. - * @return the Completable instance + * @param supplier the supplier that returns the {@code Completable} that will be subscribed to. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code supplier} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable defer(final Supplier completableSupplier) { - ObjectHelper.requireNonNull(completableSupplier, "completableSupplier"); - return RxJavaPlugins.onAssembly(new CompletableDefer(completableSupplier)); + public static Completable defer(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new CompletableDefer(supplier)); } /** - * Creates a Completable which calls the given error supplier for each subscriber - * and emits its returned Throwable. + * Creates a {@code Completable} which calls the given error supplier for each subscriber + * and emits its returned {@link Throwable}. *

* *

- * If the errorSupplier returns null, the child CompletableObservers will receive a - * NullPointerException. + * If the {@code errorSupplier} returns {@code null}, the downstream {@link CompletableObserver}s will receive a + * {@link NullPointerException}. *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
- * @param errorSupplier the error supplier, not null - * @return the new Completable instance - * @throws NullPointerException if errorSupplier is null + * @param supplier the error supplier, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code supplier} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable error(final Supplier errorSupplier) { - ObjectHelper.requireNonNull(errorSupplier, "errorSupplier is null"); - return RxJavaPlugins.onAssembly(new CompletableErrorSupplier(errorSupplier)); + public static Completable error(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new CompletableErrorSupplier(supplier)); } /** - * Creates a Completable instance that emits the given Throwable exception to subscribers. + * Creates a {@code Completable} instance that emits the given {@link Throwable} exception to subscribers. *

* *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
- * @param error the Throwable instance to emit, not null - * @return the new Completable instance - * @throws NullPointerException if error is null + * @param throwable the {@code Throwable} instance to emit, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code throwable} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable error(final Throwable error) { - ObjectHelper.requireNonNull(error, "error is null"); - return RxJavaPlugins.onAssembly(new CompletableError(error)); + public static Completable error(@NonNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is null"); + return RxJavaPlugins.onAssembly(new CompletableError(throwable)); } /** - * Returns a Completable instance that runs the given Action for each subscriber and - * emits either an unchecked exception or simply completes. + * Returns a {@code Completable} instance that runs the given {@link Action} for each {@link CompletableObserver} and + * emits either an exception or simply completes. *

* *

*
Scheduler:
*
{@code fromAction} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@link Action} throws an exception, the respective {@link Throwable} is + *
If the {@code Action} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link CompletableObserver#onError(Throwable)}, * except when the downstream has disposed this {@code Completable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. *
*
- * @param run the runnable to run for each subscriber - * @return the new Completable instance - * @throws NullPointerException if run is null + * @param action the {@code Action} to run for each subscribing {@code CompletableObserver} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code action} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromAction(final Action run) { - ObjectHelper.requireNonNull(run, "run is null"); - return RxJavaPlugins.onAssembly(new CompletableFromAction(run)); + public static Completable fromAction(@NonNull Action action) { + Objects.requireNonNull(action, "action is null"); + return RxJavaPlugins.onAssembly(new CompletableFromAction(action)); } /** - * Returns a Completable which when subscribed, executes the callable function, ignores its - * normal result and emits onError or onComplete only. + * Returns a {@code Completable} which when subscribed, executes the {@link Callable} function, ignores its + * normal result and emits {@code onError} or {@code onComplete} only. *

* *

*
Scheduler:
*
{@code fromCallable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@link Callable} throws an exception, the respective {@link Throwable} is + *
If the {@code Callable} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link CompletableObserver#onError(Throwable)}, * except when the downstream has disposed this {@code Completable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. *
*
- * @param callable the callable instance to execute for each subscriber - * @return the new Completable instance + * @param callable the {@code Callable} instance to execute for each subscribing {@link CompletableObserver} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code callable} is {@code null} * @see #defer(Supplier) * @see #fromSupplier(Supplier) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromCallable(final Callable callable) { - ObjectHelper.requireNonNull(callable, "callable is null"); + public static Completable fromCallable(@NonNull Callable callable) { + Objects.requireNonNull(callable, "callable is null"); return RxJavaPlugins.onAssembly(new CompletableFromCallable(callable)); } /** - * Returns a Completable instance that reacts to the termination of the given Future in a blocking fashion. + * Returns a {@code Completable} instance that reacts to the termination of the given {@link Future} in a blocking fashion. *

* *

- * Note that if any of the observers to this Completable call dispose, this Completable will cancel the future. + * Note that disposing the {@code Completable} won't cancel the {@code Future}. + * Use {@link #doOnDispose(Action)} and call {@link Future#cancel(boolean)} in the + * {@link Action}. *

*
Scheduler:
*
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
*
- * @param future the future to react to - * @return the new Completable instance + * @param future the {@code Future} to react to + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code future} is {@code null} + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromFuture(final Future future) { - ObjectHelper.requireNonNull(future, "future is null"); + public static Completable fromFuture(@NonNull Future future) { + Objects.requireNonNull(future, "future is null"); return fromAction(Functions.futureAction(future)); } /** - * Returns a Completable instance that when subscribed to, subscribes to the {@code Maybe} instance and - * emits a completion event if the maybe emits {@code onSuccess}/{@code onComplete} or forwards any + * Returns a {@code Completable} instance that when subscribed to, subscribes to the {@link MaybeSource} instance and + * emits an {@code onComplete} event if the maybe emits {@code onSuccess}/{@code onComplete} or forwards any * {@code onError} events. *

* @@ -493,50 +622,56 @@ public static Completable fromFuture(final Future future) { *

{@code fromMaybe} does not operate by default on a particular {@link Scheduler}.
* *

History: 2.1.17 - beta - * @param the value type of the {@link MaybeSource} element - * @param maybe the Maybe instance to subscribe to, not null - * @return the new Completable instance - * @throws NullPointerException if single is null + * @param the value type of the {@code MaybeSource} element + * @param maybe the {@code MaybeSource} instance to subscribe to, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code maybe} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromMaybe(final MaybeSource maybe) { - ObjectHelper.requireNonNull(maybe, "maybe is null"); - return RxJavaPlugins.onAssembly(new MaybeIgnoreElementCompletable(maybe)); + public static <@NonNull T> Completable fromMaybe(@NonNull MaybeSource maybe) { + Objects.requireNonNull(maybe, "maybe is null"); + return RxJavaPlugins.onAssembly(new MaybeIgnoreElementCompletable<>(maybe)); } /** - * Returns a Completable instance that runs the given Runnable for each subscriber and - * emits either its exception or simply completes. + * Returns a {@code Completable} instance that runs the given {@link Runnable} for each {@link CompletableObserver} and + * emits either its unchecked exception or simply completes. *

* + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. *

*
Scheduler:
*
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@link Runnable} throws an exception, the respective {@link Throwable} is + *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is * delivered to the downstream via {@link CompletableObserver#onError(Throwable)}, * except when the downstream has disposed this {@code Completable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. *
*
- * @param run the runnable to run for each subscriber - * @return the new Completable instance - * @throws NullPointerException if run is null + * @param run the {@code Runnable} to run for each {@code CompletableObserver} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code run} is {@code null} + * @see #fromAction(Action) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromRunnable(final Runnable run) { - ObjectHelper.requireNonNull(run, "run is null"); + public static Completable fromRunnable(@NonNull Runnable run) { + Objects.requireNonNull(run, "run is null"); return RxJavaPlugins.onAssembly(new CompletableFromRunnable(run)); } /** - * Returns a Completable instance that subscribes to the given Observable, ignores all values and + * Returns a {@code Completable} instance that subscribes to the given {@link ObservableSource}, ignores all values and * emits only the terminal event. *

* @@ -544,33 +679,33 @@ public static Completable fromRunnable(final Runnable run) { *

Scheduler:
*
{@code fromObservable} does not operate by default on a particular {@link Scheduler}.
* - * @param the type of the Observable - * @param observable the Observable instance to subscribe to, not null - * @return the new Completable instance - * @throws NullPointerException if flowable is null + * @param the type of the {@code ObservableSource} + * @param observable the {@code ObservableSource} instance to subscribe to, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code observable} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromObservable(final ObservableSource observable) { - ObjectHelper.requireNonNull(observable, "observable is null"); - return RxJavaPlugins.onAssembly(new CompletableFromObservable(observable)); + public static <@NonNull T> Completable fromObservable(@NonNull ObservableSource observable) { + Objects.requireNonNull(observable, "observable is null"); + return RxJavaPlugins.onAssembly(new CompletableFromObservable<>(observable)); } /** - * Returns a Completable instance that subscribes to the given publisher, ignores all values and + * Returns a {@code Completable} instance that subscribes to the given {@link Publisher}, ignores all values and * emits only the terminal event. *

* *

- * The {@link Publisher} must follow the + * The {@code Publisher} must follow the * Reactive-Streams specification. * Violating the specification may result in undefined behavior. *

* If possible, use {@link #create(CompletableOnSubscribe)} to create a * source-like {@code Completable} instead. *

- * Note that even though {@link Publisher} appears to be a functional interface, it + * Note that even though {@code Publisher} appears to be a functional interface, it * is not recommended to implement it through a lambda as the specification requires * state management that is not achievable with a stateless lambda. *

@@ -580,61 +715,62 @@ public static Completable fromObservable(final ObservableSource observabl *
Scheduler:
*
{@code fromPublisher} does not operate by default on a particular {@link Scheduler}.
*
- * @param the type of the publisher - * @param publisher the Publisher instance to subscribe to, not null - * @return the new Completable instance - * @throws NullPointerException if publisher is null + * @param the type of the {@code Publisher} + * @param publisher the {@code Publisher} instance to subscribe to, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code publisher} is {@code null} * @see #create(CompletableOnSubscribe) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromPublisher(final Publisher publisher) { - ObjectHelper.requireNonNull(publisher, "publisher is null"); - return RxJavaPlugins.onAssembly(new CompletableFromPublisher(publisher)); + public static <@NonNull T> Completable fromPublisher(@NonNull Publisher publisher) { + Objects.requireNonNull(publisher, "publisher is null"); + return RxJavaPlugins.onAssembly(new CompletableFromPublisher<>(publisher)); } /** - * Returns a Completable instance that when subscribed to, subscribes to the Single instance and - * emits a completion event if the single emits onSuccess or forwards any onError events. + * Returns a {@code Completable} instance that when subscribed to, subscribes to the {@link SingleSource} instance and + * emits a completion event if the single emits {@code onSuccess} or forwards any {@code onError} events. *

* *

*
Scheduler:
*
{@code fromSingle} does not operate by default on a particular {@link Scheduler}.
*
- * @param the value type of the Single - * @param single the Single instance to subscribe to, not null - * @return the new Completable instance - * @throws NullPointerException if single is null + * @param the value type of the {@code SingleSource} + * @param single the {@code SingleSource} instance to subscribe to, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code single} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromSingle(final SingleSource single) { - ObjectHelper.requireNonNull(single, "single is null"); - return RxJavaPlugins.onAssembly(new CompletableFromSingle(single)); + public static <@NonNull T> Completable fromSingle(@NonNull SingleSource single) { + Objects.requireNonNull(single, "single is null"); + return RxJavaPlugins.onAssembly(new CompletableFromSingle<>(single)); } /** - * Returns a Completable which when subscribed, executes the supplier function, ignores its - * normal result and emits onError or onComplete only. + * Returns a {@code Completable} which when subscribed, executes the {@link Supplier} function, ignores its + * normal result and emits {@code onError} or {@code onComplete} only. *

* *

*
Scheduler:
*
{@code fromSupplier} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@link Supplier} throws an exception, the respective {@link Throwable} is + *
If the {@code Supplier} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link CompletableObserver#onError(Throwable)}, * except when the downstream has disposed this {@code Completable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. *
*
- * @param supplier the Supplier instance to execute for each subscriber - * @return the new Completable instance + * @param supplier the {@code Supplier} instance to execute for each {@link CompletableObserver} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see #defer(Supplier) * @see #fromCallable(Callable) * @since 3.0.0 @@ -642,43 +778,44 @@ public static Completable fromSingle(final SingleSource single) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable fromSupplier(final Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); + public static Completable fromSupplier(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); return RxJavaPlugins.onAssembly(new CompletableFromSupplier(supplier)); } /** - * Returns a Completable instance that subscribes to all sources at once and - * completes only when all source Completables complete or one of them emits an error. + * Returns a {@code Completable} instance that subscribes to all sources at once and + * completes only when all source {@link CompletableSource}s complete or one of them emits an error. *

* *

*
Scheduler:
*
{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code CompletableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code CompletableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Completable} terminates with that {@code Throwable} and all other source {@code CompletableSource}s are disposed. * If more than one {@code CompletableSource} signals an error, the resulting {@code Completable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Completable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeArrayDelayError(CompletableSource...)} to merge sources and terminate only when all source {@code CompletableSource}s * have completed or failed with an error. *
*
- * @param sources the iterable sequence of sources. - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the array of {@code CompletableSource}s. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeArrayDelayError(CompletableSource...) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable mergeArray(CompletableSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static Completable mergeArray(@NonNull CompletableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return complete(); } else @@ -689,147 +826,151 @@ public static Completable mergeArray(CompletableSource... sources) { } /** - * Returns a Completable instance that subscribes to all sources at once and - * completes only when all source Completables complete or one of them emits an error. + * Returns a {@code Completable} instance that subscribes to all sources at once and + * completes only when all source {@link CompletableSource}s complete or one of them emits an error. *

* *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code CompletableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code CompletableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Completable} terminates with that {@code Throwable} and all other source {@code CompletableSource}s are disposed. * If more than one {@code CompletableSource} signals an error, the resulting {@code Completable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Completable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable)} to merge sources and terminate only when all source {@code CompletableSource}s * have completed or failed with an error. *
*
- * @param sources the iterable sequence of sources. - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the {@link Iterable} sequence of {@code CompletableSource}s. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeDelayError(Iterable) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable merge(Iterable sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static Completable merge(@NonNull Iterable<@NonNull ? extends CompletableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return RxJavaPlugins.onAssembly(new CompletableMergeIterable(sources)); } /** - * Returns a Completable instance that subscribes to all sources at once and - * completes only when all source Completables complete or one of them emits an error. + * Returns a {@code Completable} instance that subscribes to all sources at once and + * completes only when all source {@link CompletableSource}s complete or one of them emits an error. *

* *

*
Backpressure:
- *
The returned {@code Completable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well.
+ *
The operator consumes the given {@link Publisher} in an unbounded manner + * (requesting {@link Long#MAX_VALUE} upfront).
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code CompletableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code CompletableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Completable} terminates with that {@code Throwable} and all other source {@code CompletableSource}s are disposed. * If more than one {@code CompletableSource} signals an error, the resulting {@code Completable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Completable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher)} to merge sources and terminate only when all source {@code CompletableSource}s * have completed or failed with an error. *
*
- * @param sources the iterable sequence of sources. - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the {@code Publisher} sequence of {@code CompletableSource}s. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeDelayError(Publisher) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) - public static Completable merge(Publisher sources) { + @NonNull + public static Completable merge(@NonNull Publisher<@NonNull ? extends CompletableSource> sources) { return merge0(sources, Integer.MAX_VALUE, false); } /** - * Returns a Completable instance that keeps subscriptions to a limited number of sources at once and - * completes only when all source Completables complete or one of them emits an error. + * Returns a {@code Completable} instance that keeps subscriptions to a limited number of sources at once and + * completes only when all source {@link CompletableSource}s complete or one of them emits an error. *

* *

*
Backpressure:
- *
The returned {@code Completable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well.
+ *
The operator consumes the given {@link Publisher} in a bounded manner, + * requesting {@code maxConcurrency} items first, then keeps requesting as + * many more as the inner {@code CompletableSource}s terminate.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code CompletableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code CompletableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Completable} terminates with that {@code Throwable} and all other source {@code CompletableSource}s are disposed. * If more than one {@code CompletableSource} signals an error, the resulting {@code Completable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Completable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher, int)} to merge sources and terminate only when all source {@code CompletableSource}s * have completed or failed with an error. *
*
- * @param sources the iterable sequence of sources. + * @param sources the {@code Publisher} sequence of {@code CompletableSource}s. * @param maxConcurrency the maximum number of concurrent subscriptions - * @return the new Completable instance - * @throws NullPointerException if sources is null - * @throws IllegalArgumentException if maxConcurrency is less than 1 + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is less than 1 * @see #mergeDelayError(Publisher, int) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Completable merge(Publisher sources, int maxConcurrency) { + @NonNull + public static Completable merge(@NonNull Publisher<@NonNull ? extends CompletableSource> sources, int maxConcurrency) { return merge0(sources, maxConcurrency, false); } /** - * Returns a Completable instance that keeps subscriptions to a limited number of sources at once and - * completes only when all source Completables terminate in one way or another, combining any exceptions - * thrown by either the sources Observable or the inner Completable instances. + * Returns a {@code Completable} instance that keeps subscriptions to a limited number of {@link CompletableSource}s at once and + * completes only when all source {@code CompletableSource}s terminate in one way or another, combining any exceptions + * signaled by either the source {@link Publisher} or the inner {@code CompletableSource} instances. *
*
Backpressure:
- *
The returned {@code Flowable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well. + *
The operator consumes the given {@code Publisher} in a bounded manner, + * requesting {@code maxConcurrency} items first, then keeps requesting as + * many more as the inner {@code CompletableSource}s terminate.
*
Scheduler:
*
{@code merge0} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the iterable sequence of sources. + * @param sources the {@code Publisher} sequence of {@code CompletableSource}s. * @param maxConcurrency the maximum number of concurrent subscriptions - * @param delayErrors delay all errors from the main source and from the inner Completables? - * @return the new Completable instance - * @throws NullPointerException if sources is null - * @throws IllegalArgumentException if maxConcurrency is less than 1 + * @param delayErrors delay all errors from the main source and from the inner {@code CompletableSource}s? + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is less than 1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - private static Completable merge0(Publisher sources, int maxConcurrency, boolean delayErrors) { - ObjectHelper.requireNonNull(sources, "sources is null"); + private static Completable merge0(@NonNull Publisher<@NonNull ? extends CompletableSource> sources, int maxConcurrency, boolean delayErrors) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); return RxJavaPlugins.onAssembly(new CompletableMerge(sources, maxConcurrency, delayErrors)); } /** - * Returns a CompletableConsumable that subscribes to all Completables in the source array and delays - * any error emitted by either the sources observable or any of the inner Completables until all of + * Returns a {@code Completable} that subscribes to all {@link CompletableSource}s in the source array and delays + * any error emitted by any of the inner {@code CompletableSource}s until all of * them terminate in a way or another. *

* @@ -837,108 +978,114 @@ private static Completable merge0(Publisher sources *

Scheduler:
*
{@code mergeArrayDelayError} does not operate by default on a particular {@link Scheduler}.
* - * @param sources the array of Completables - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the array of {@code CompletableSource}s + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable mergeArrayDelayError(CompletableSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new CompletableMergeDelayErrorArray(sources)); + @SafeVarargs + public static Completable mergeArrayDelayError(@NonNull CompletableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new CompletableMergeArrayDelayError(sources)); } /** - * Returns a Completable that subscribes to all Completables in the source sequence and delays - * any error emitted by either the sources observable or any of the inner Completables until all of + * Returns a {@code Completable} that subscribes to all {@link CompletableSource}s in the source sequence and delays + * any error emitted by any of the inner {@code CompletableSource}s until all of * them terminate in a way or another. *

- * + * *

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the sequence of Completables - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the sequence of {@code CompletableSource}s + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable mergeDelayError(Iterable sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static Completable mergeDelayError(@NonNull Iterable<@NonNull ? extends CompletableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return RxJavaPlugins.onAssembly(new CompletableMergeDelayErrorIterable(sources)); } /** - * Returns a Completable that subscribes to all Completables in the source sequence and delays - * any error emitted by either the sources observable or any of the inner Completables until all of + * Returns a {@code Completable} that subscribes to all {@link CompletableSource}s in the source sequence and delays + * any error emitted by either the sources {@link Publisher} or any of the inner {@code CompletableSource}s until all of * them terminate in a way or another. *

* *

*
Backpressure:
- *
The returned {@code Completable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well.
+ *
The operator consumes the {@code Publisher} in an unbounded manner + * (requesting {@link Long#MAX_VALUE} from it).
*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the sequence of Completables - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the sequence of {@code CompletableSource}s + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) - public static Completable mergeDelayError(Publisher sources) { + @NonNull + public static Completable mergeDelayError(@NonNull Publisher<@NonNull ? extends CompletableSource> sources) { return merge0(sources, Integer.MAX_VALUE, true); } /** - * Returns a Completable that subscribes to a limited number of inner Completables at once in + * Returns a {@code Completable} that subscribes to a limited number of inner {@link CompletableSource}s at once in * the source sequence and delays any error emitted by either the sources - * observable or any of the inner Completables until all of + * {@link Publisher} or any of the inner {@code CompletableSource}s until all of * them terminate in a way or another. *

* *

*
Backpressure:
- *
The returned {@code Completable} honors the backpressure of the downstream consumer - * and expects the other {@code Publisher} to honor it as well.
+ *
The operator requests {@code maxConcurrency} items from the {@code Publisher} + * upfront and keeps requesting as many more as many inner {@code CompletableSource}s terminate.
*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the sequence of Completables - * @param maxConcurrency the maximum number of concurrent subscriptions to Completables - * @return the new Completable instance - * @throws NullPointerException if sources is null + * @param sources the sequence of {@code CompletableSource}s + * @param maxConcurrency the maximum number of concurrent subscriptions to have + * at a time to the inner {@code CompletableSource}s + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Completable mergeDelayError(Publisher sources, int maxConcurrency) { + @NonNull + public static Completable mergeDelayError(@NonNull Publisher<@NonNull ? extends CompletableSource> sources, int maxConcurrency) { return merge0(sources, maxConcurrency, true); } /** - * Returns a Completable that never calls onError or onComplete. + * Returns a {@code Completable} that never calls {@code onError} or {@code onComplete}. *

* *

*
Scheduler:
*
{@code never} does not operate by default on a particular {@link Scheduler}.
*
- * @return the singleton instance that never calls onError or onComplete + * @return the singleton instance that never calls {@code onError} or {@code onComplete} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public static Completable never() { return RxJavaPlugins.onAssembly(CompletableNever.INSTANCE); } /** - * Returns a Completable instance that fires its onComplete event after the given delay elapsed. + * Returns a {@code Completable} instance that fires its {@code onComplete} event after the given delay elapsed. *

* *

@@ -947,41 +1094,44 @@ public static Completable never() { *
* @param delay the delay time * @param unit the delay unit - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Completable timer(long delay, TimeUnit unit) { + @NonNull + public static Completable timer(long delay, @NonNull TimeUnit unit) { return timer(delay, unit, Schedulers.computation()); } /** - * Returns a Completable instance that fires its onComplete event after the given delay elapsed - * by using the supplied scheduler. + * Returns a {@code Completable} instance that fires its {@code onComplete} event after the given delay elapsed + * by using the supplied {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code timer} operates on the {@link Scheduler} you specify.
+ *
{@code timer} operates on the {@code Scheduler} you specify.
*
* @param delay the delay time * @param unit the delay unit - * @param scheduler the scheduler where to emit the complete event - * @return the new Completable instance + * @param scheduler the {@code Scheduler} where to emit the {@code onComplete} event + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Completable timer(final long delay, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public static Completable timer(long delay, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new CompletableTimer(delay, unit, scheduler)); } /** - * Creates a NullPointerException instance and sets the given Throwable as its initial cause. - * @param ex the Throwable instance to use as cause, not null (not verified) - * @return the created NullPointerException + * Creates a {@link NullPointerException} instance and sets the given {@link Throwable} as its initial cause. + * @param ex the {@code Throwable} instance to use as cause, not {@code null} (not verified) + * @return the new {@code NullPointerException} */ private static NullPointerException toNpe(Throwable ex) { NullPointerException npe = new NullPointerException("Actually not, but can't pass out an exception otherwise..."); @@ -990,10 +1140,75 @@ private static NullPointerException toNpe(Throwable ex) { } /** - * Returns a Completable instance which manages a resource along - * with a custom Completable instance while the subscription is active. + * Switches between {@link CompletableSource}s emitted by the source {@link Publisher} whenever + * a new {@code CompletableSource} is emitted, disposing the previously running {@code CompletableSource}, + * exposing the setup as a {@code Completable} sequence. + *

+ * + *

+ *
Backpressure:
+ *
The {@code sources} {@code Publisher} is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
The returned sequence fails with the first error signaled by the {@code sources} {@code Publisher} + * or the currently running {@code CompletableSource}, disposing the rest. Late errors are + * forwarded to the global error handler via {@link RxJavaPlugins#onError(Throwable)}.
+ *
+ * @param sources the {@code Publisher} sequence of inner {@code CompletableSource}s to switch between + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + * @see #switchOnNextDelayError(Publisher) + * @see ReactiveX operators documentation: Switch + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + public static Completable switchOnNext(@NonNull Publisher<@NonNull ? extends CompletableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapCompletablePublisher<>(sources, Functions.identity(), false)); + } + + /** + * Switches between {@link CompletableSource}s emitted by the source {@link Publisher} whenever + * a new {@code CompletableSource} is emitted, disposing the previously running {@code CompletableSource}, + * exposing the setup as a {@code Completable} sequence and delaying all errors from + * all of them until all terminate. + *

+ * + *

+ *
Backpressure:
+ *
The {@code sources} {@code Publisher} is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
The returned {@code Completable} collects all errors emitted by either the {@code sources} + * {@code Publisher} or any inner {@code CompletableSource} and emits them as a {@link CompositeException} + * when all sources terminate. If only one source ever failed, its error is emitted as-is at the end.
+ *
+ * @param sources the {@code Publisher} sequence of inner {@code CompletableSource}s to switch between + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + * @see #switchOnNext(Publisher) + * @see ReactiveX operators documentation: Switch + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + public static Completable switchOnNextDelayError(@NonNull Publisher<@NonNull ? extends CompletableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapCompletablePublisher<>(sources, Functions.identity(), true)); + } + + /** + * Returns a {@code Completable} instance which manages a resource along + * with a custom {@link CompletableSource} instance while the subscription is active. *

- * + * *

* This overload disposes eagerly before the terminal event is emitted. *

@@ -1001,62 +1216,67 @@ private static NullPointerException toNpe(Throwable ex) { *
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* @param the resource type - * @param resourceSupplier the supplier that returns a resource to be managed. - * @param completableFunction the function that given a resource returns a Completable instance that will be subscribed to - * @param disposer the consumer that disposes the resource created by the resource supplier - * @return the new Completable instance + * @param resourceSupplier the {@link Supplier} that returns a resource to be managed. + * @param sourceSupplier the {@link Function} that given a resource returns a {@code CompletableSource} instance that will be subscribed to + * @param resourceCleanup the {@link Consumer} that disposes the resource created by the resource supplier + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} + * or {@code resourceCleanup} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Completable using(Supplier resourceSupplier, - Function completableFunction, - Consumer disposer) { - return using(resourceSupplier, completableFunction, disposer, true); + @NonNull + public static <@NonNull R> Completable using(@NonNull Supplier resourceSupplier, + @NonNull Function sourceSupplier, + @NonNull Consumer resourceCleanup) { + return using(resourceSupplier, sourceSupplier, resourceCleanup, true); } /** - * Returns a Completable instance which manages a resource along - * with a custom Completable instance while the subscription is active and performs eager or lazy + * Returns a {@code Completable} instance which manages a resource along + * with a custom {@link CompletableSource} instance while the subscription is active and performs eager or lazy * resource disposition. *

* *

* If this overload performs a lazy disposal after the terminal event is emitted. - * Exceptions thrown at this time will be delivered to RxJavaPlugins only. + * The exceptions thrown at this time will be delivered to the global {@link RxJavaPlugins#onError(Throwable)} handler only. *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* @param the resource type - * @param resourceSupplier the supplier that returns a resource to be managed - * @param completableFunction the function that given a resource returns a non-null - * Completable instance that will be subscribed to - * @param disposer the consumer that disposes the resource created by the resource supplier + * @param resourceSupplier the {@link Supplier} that returns a resource to be managed + * @param sourceSupplier the {@link Function} that given a resource returns a non-{@code null} + * {@code CompletableSource} instance that will be subscribed to + * @param resourceCleanup the {@link Consumer} that disposes the resource created by the resource supplier * @param eager * If {@code true} then resource disposal will happen either on a {@code dispose()} call before the upstream is disposed * or just before the emission of a terminal event ({@code onComplete} or {@code onError}). * If {@code false} the resource disposal will happen either on a {@code dispose()} call after the upstream is disposed * or just after the emission of a terminal event ({@code onComplete} or {@code onError}). - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} + * or {@code resourceCleanup} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable using( - final Supplier resourceSupplier, - final Function completableFunction, - final Consumer disposer, - final boolean eager) { - ObjectHelper.requireNonNull(resourceSupplier, "resourceSupplier is null"); - ObjectHelper.requireNonNull(completableFunction, "completableFunction is null"); - ObjectHelper.requireNonNull(disposer, "disposer is null"); + public static <@NonNull R> Completable using( + @NonNull Supplier resourceSupplier, + @NonNull Function sourceSupplier, + @NonNull Consumer resourceCleanup, + boolean eager) { + Objects.requireNonNull(resourceSupplier, "resourceSupplier is null"); + Objects.requireNonNull(sourceSupplier, "sourceSupplier is null"); + Objects.requireNonNull(resourceCleanup, "resourceCleanup is null"); - return RxJavaPlugins.onAssembly(new CompletableUsing(resourceSupplier, completableFunction, disposer, eager)); + return RxJavaPlugins.onAssembly(new CompletableUsing<>(resourceSupplier, sourceSupplier, resourceCleanup, eager)); } /** - * Wraps the given CompletableSource into a Completable - * if not already Completable. + * Wraps the given {@link CompletableSource} into a {@code Completable} + * if not already {@code Completable}. *

* *

@@ -1064,14 +1284,14 @@ public static Completable using( *
{@code wrap} does not operate by default on a particular {@link Scheduler}.
*
* @param source the source to wrap - * @return the source or its wrapper Completable - * @throws NullPointerException if source is null + * @return the new wrapped or cast {@code Completable} instance + * @throws NullPointerException if {@code source} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Completable wrap(CompletableSource source) { - ObjectHelper.requireNonNull(source, "source is null"); + public static Completable wrap(@NonNull CompletableSource source) { + Objects.requireNonNull(source, "source is null"); if (source instanceof Completable) { return RxJavaPlugins.onAssembly((Completable)source); } @@ -1079,56 +1299,56 @@ public static Completable wrap(CompletableSource source) { } /** - * Returns a Completable that emits the a terminated event of either this Completable - * or the other Completable whichever fires first. + * Returns a {@code Completable} that emits the a terminated event of either this {@code Completable} + * or the other {@link CompletableSource}, whichever fires first. *

- * + * *

*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param other the other Completable, not null. A subscription to this provided source will occur after subscribing + * @param other the other {@code CompletableSource}, not {@code null}. A subscription to this provided source will occur after subscribing * to the current source. - * @return the new Completable instance - * @throws NullPointerException if other is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code other} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable ambWith(CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Completable ambWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); return ambArray(this, other); } /** - * Returns an Observable which will subscribe to this Completable and once that is completed then - * will subscribe to the {@code next} ObservableSource. An error event from this Completable will be - * propagated to the downstream subscriber and will result in skipping the subscription of the - * Observable. + * Returns an {@link Observable} which will subscribe to this {@code Completable} and once that is completed then + * will subscribe to the {@code next} {@link ObservableSource}. An error event from this {@code Completable} will be + * propagated to the downstream observer and will result in skipping the subscription to the + * next {@code ObservableSource}. *

* *

*
Scheduler:
*
{@code andThen} does not operate by default on a particular {@link Scheduler}.
*
- * @param the value type of the next ObservableSource - * @param next the Observable to subscribe after this Completable is completed, not null - * @return Observable that composes this Completable and next - * @throws NullPointerException if next is null + * @param the value type of the next {@code ObservableSource} + * @param next the {@code ObservableSource} to subscribe after this {@code Completable} is completed, not {@code null} + * @return the new {@code Observable} that composes this {@code Completable} and the next {@code ObservableSource} + * @throws NullPointerException if {@code next} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Observable andThen(ObservableSource next) { - ObjectHelper.requireNonNull(next, "next is null"); - return RxJavaPlugins.onAssembly(new CompletableAndThenObservable(this, next)); + public final <@NonNull T> Observable andThen(@NonNull ObservableSource next) { + Objects.requireNonNull(next, "next is null"); + return RxJavaPlugins.onAssembly(new CompletableAndThenObservable<>(this, next)); } /** - * Returns a Flowable which will subscribe to this Completable and once that is completed then - * will subscribe to the {@code next} Flowable. An error event from this Completable will be - * propagated to the downstream subscriber and will result in skipping the subscription of the - * Publisher. + * Returns a {@link Flowable} which will subscribe to this {@code Completable} and once that is completed then + * will subscribe to the {@code next} {@link Publisher}. An error event from this {@code Completable} will be + * propagated to the downstream subscriber and will result in skipping the subscription to the next + * {@code Publisher}. *

* *

@@ -1138,25 +1358,25 @@ public final Observable andThen(ObservableSource next) { *
Scheduler:
*
{@code andThen} does not operate by default on a particular {@link Scheduler}.
*
- * @param the value type of the next Publisher - * @param next the Publisher to subscribe after this Completable is completed, not null - * @return Flowable that composes this Completable and next - * @throws NullPointerException if next is null + * @param the value type of the next {@code Publisher} + * @param next the {@code Publisher} to subscribe after this {@code Completable} is completed, not {@code null} + * @return the new {@code Flowable} that composes this {@code Completable} and the next {@code Publisher} + * @throws NullPointerException if {@code next} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable andThen(Publisher next) { - ObjectHelper.requireNonNull(next, "next is null"); - return RxJavaPlugins.onAssembly(new CompletableAndThenPublisher(this, next)); + public final <@NonNull T> Flowable andThen(@NonNull Publisher next) { + Objects.requireNonNull(next, "next is null"); + return RxJavaPlugins.onAssembly(new CompletableAndThenPublisher<>(this, next)); } /** - * Returns a Single which will subscribe to this Completable and once that is completed then - * will subscribe to the {@code next} SingleSource. An error event from this Completable will be - * propagated to the downstream subscriber and will result in skipping the subscription of the - * Single. + * Returns a {@link Single} which will subscribe to this {@code Completable} and once that is completed then + * will subscribe to the {@code next} {@link SingleSource}. An error event from this {@code Completable} will be + * propagated to the downstream observer and will result in skipping the subscription to the next + * {@code SingleSource}. *

* *

@@ -1164,45 +1384,49 @@ public final Flowable andThen(Publisher next) { *
{@code andThen} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the next SingleSource - * @param next the Single to subscribe after this Completable is completed, not null - * @return Single that composes this Completable and next + * @param the value type of the next {@code SingleSource} + * @param next the {@code SingleSource} to subscribe after this {@code Completable} is completed, not {@code null} + * @return the new {@code Single} that composes this {@code Completable} and the next {@code SingleSource} + * @throws NullPointerException if {@code next} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single andThen(SingleSource next) { - ObjectHelper.requireNonNull(next, "next is null"); - return RxJavaPlugins.onAssembly(new SingleDelayWithCompletable(next, this)); + public final <@NonNull T> Single andThen(@NonNull SingleSource next) { + Objects.requireNonNull(next, "next is null"); + return RxJavaPlugins.onAssembly(new SingleDelayWithCompletable<>(next, this)); } /** - * Returns a {@link Maybe} which will subscribe to this Completable and once that is completed then - * will subscribe to the {@code next} MaybeSource. An error event from this Completable will be - * propagated to the downstream subscriber and will result in skipping the subscription of the - * Maybe. + * Returns a {@link Maybe} which will subscribe to this {@code Completable} and once that is completed then + * will subscribe to the {@code next} {@link MaybeSource}. An error event from this {@code Completable} will be + * propagated to the downstream observer and will result in skipping the subscription to the next + * {@code MaybeSource}. *

- * + * *

*
Scheduler:
*
{@code andThen} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the next MaybeSource - * @param next the Maybe to subscribe after this Completable is completed, not null - * @return Maybe that composes this Completable and next + * @param the value type of the next {@code MaybeSource} + * @param next the {@code MaybeSource} to subscribe after this {@code Completable} is completed, not {@code null} + * @return the new {@code Maybe} that composes this {@code Completable} and the next {@code MaybeSource} + * @throws NullPointerException if {@code next} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe andThen(MaybeSource next) { - ObjectHelper.requireNonNull(next, "next is null"); - return RxJavaPlugins.onAssembly(new MaybeDelayWithCompletable(next, this)); + public final <@NonNull T> Maybe andThen(@NonNull MaybeSource next) { + Objects.requireNonNull(next, "next is null"); + return RxJavaPlugins.onAssembly(new MaybeDelayWithCompletable<>(next, this)); } /** - * Returns a Completable that first runs this Completable - * and then the other completable. + * Returns a {@code Completable} that first runs this {@code Completable} + * and then the other {@link CompletableSource}. An error event from this {@code Completable} will be + * propagated to the downstream observer and will result in skipping the subscription to the next + * {@code CompletableSource}. *

* *

@@ -1211,22 +1435,23 @@ public final Maybe andThen(MaybeSource next) { *

Scheduler:
*
{@code andThen} does not operate by default on a particular {@link Scheduler}.
* - * @param next the other Completable, not null - * @return the new Completable instance - * @throws NullPointerException if other is null + * @param next the other {@code CompletableSource}, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code next} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable andThen(CompletableSource next) { - ObjectHelper.requireNonNull(next, "next is null"); + @NonNull + public final Completable andThen(@NonNull CompletableSource next) { + Objects.requireNonNull(next, "next is null"); return RxJavaPlugins.onAssembly(new CompletableAndThenCompletable(this, next)); } /** - * Subscribes to and awaits the termination of this Completable instance in a blocking manner and + * Subscribes to and awaits the termination of this {@code Completable} instance in a blocking manner and * rethrows any exception emitted. *

- * + * *

*
Scheduler:
*
{@code blockingAwait} does not operate by default on a particular {@link Scheduler}.
@@ -1235,17 +1460,17 @@ public final Completable andThen(CompletableSource next) { * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and * {@link Error}s are rethrown as they are. *
- * @throws RuntimeException wrapping an InterruptedException if the current thread is interrupted + * @throws RuntimeException wrapping an {@link InterruptedException} if the current thread is interrupted */ @SchedulerSupport(SchedulerSupport.NONE) public final void blockingAwait() { - BlockingMultiObserver observer = new BlockingMultiObserver(); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); subscribe(observer); observer.blockingGet(); } /** - * Subscribes to and awaits the termination of this Completable instance in a blocking manner + * Subscribes to and awaits the termination of this {@code Completable} instance in a blocking manner * with a specific timeout and rethrows any exception emitted within the timeout window. *

* @@ -1259,23 +1484,123 @@ public final void blockingAwait() { * * @param timeout the timeout value * @param unit the timeout unit - * @return true if the this Completable instance completed normally within the time limit, - * false if the timeout elapsed before this Completable terminated. - * @throws RuntimeException wrapping an InterruptedException if the current thread is interrupted + * @return {@code true} if the this {@code Completable} instance completed normally within the time limit, + * {@code false} if the timeout elapsed before this {@code Completable} terminated. + * @throws RuntimeException wrapping an {@link InterruptedException} if the current thread is interrupted + * @throws NullPointerException if {@code unit} is {@code null} */ @CheckReturnValue - @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final boolean blockingAwait(long timeout, TimeUnit unit) { - ObjectHelper.requireNonNull(unit, "unit is null"); - BlockingMultiObserver observer = new BlockingMultiObserver(); + public final boolean blockingAwait(long timeout, @NonNull TimeUnit unit) { + Objects.requireNonNull(unit, "unit is null"); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); subscribe(observer); return observer.blockingAwait(timeout, unit); } /** - * Subscribes to this Completable only once, when the first CompletableObserver - * subscribes to the result Completable, caches its terminal event + * Subscribes to the current {@code Completable} and blocks the current thread until it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the current {@code Completable} signals an error, + * the {@link Throwable} is routed to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, an {@link InterruptedException} is routed to the same global error handler. + *
+ *
+ * @since 3.0.0 + * @see #blockingSubscribe(Action) + * @see #blockingSubscribe(Action, Consumer) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe() { + blockingSubscribe(Functions.EMPTY_ACTION, Functions.ERROR_CONSUMER); + } + + /** + * Subscribes to the current {@code Completable} and calls given {@code onComplete} callback on the current thread + * when it completes normally. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either the current {@code Completable} signals an error or {@code onComplete} throws, + * the respective {@link Throwable} is routed to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, an {@link InterruptedException} is routed to the same global error handler. + *
+ *
+ * @param onComplete the {@link Action} to call if the current {@code Completable} completes normally + * @throws NullPointerException if {@code onComplete} is {@code null} + * @since 3.0.0 + * @see #blockingSubscribe(Action, Consumer) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Action onComplete) { + blockingSubscribe(onComplete, Functions.ERROR_CONSUMER); + } + + /** + * Subscribes to the current {@code Completable} and calls the appropriate callback on the current thread + * when it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either {@code onComplete} or {@code onError} throw, the {@link Throwable} is routed to the + * global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, the {@code onError} consumer is called with an {@link InterruptedException}. + *
+ *
+ * @param onComplete the {@link Action} to call if the current {@code Completable} completes normally + * @param onError the {@link Consumer} to call if the current {@code Completable} signals an error + * @throws NullPointerException if {@code onComplete} or {@code onError} is {@code null} + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Action onComplete, @NonNull Consumer onError) { + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onError, "onError is null"); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); + subscribe(observer); + observer.blockingConsume(Functions.emptyConsumer(), onError, onComplete); + } + + /** + * Subscribes to the current {@code Completable} and calls the appropriate {@link CompletableObserver} method on the current thread. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
An {@code onError} signal is delivered to the {@link CompletableObserver#onError(Throwable)} method. + * If any of the {@code CompletableObserver}'s methods throw, the {@link RuntimeException} is propagated to the caller of this method. + * If the current thread is interrupted, an {@link InterruptedException} is delivered to {@code observer.onError}. + *
+ *
+ * @param observer the {@code CompletableObserver} to call methods on the current thread + * @throws NullPointerException if {@code observer} is {@code null} + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull CompletableObserver observer) { + Objects.requireNonNull(observer, "observer is null"); + BlockingDisposableMultiObserver blockingObserver = new BlockingDisposableMultiObserver<>(); + observer.onSubscribe(blockingObserver); + subscribe(blockingObserver); + blockingObserver.blockingConsume(observer); + } + + /** + * Subscribes to this {@code Completable} only once, when the first {@link CompletableObserver} + * subscribes to the result {@code Completable}, caches its terminal event * and relays/replays it to observers. *

* @@ -1287,45 +1612,51 @@ public final boolean blockingAwait(long timeout, TimeUnit unit) { *

{@code cache} does not operate by default on a particular {@link Scheduler}.
* *

History: 2.0.4 - experimental - * @return the new Completable instance + * @return the new {@code Completable} instance * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable cache() { return RxJavaPlugins.onAssembly(new CompletableCache(this)); } /** * Calls the given transformer function with this instance and returns the function's resulting - * Completable. + * {@link CompletableSource} wrapped with {@link #wrap(CompletableSource)}. *

* *

*
Scheduler:
*
{@code compose} does not operate by default on a particular {@link Scheduler}.
*
- * @param transformer the transformer function, not null - * @return the Completable returned by the function - * @throws NullPointerException if transformer is null + * @param transformer the transformer function, not {@code null} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code transformer} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable compose(CompletableTransformer transformer) { - return wrap(ObjectHelper.requireNonNull(transformer, "transformer is null").apply(this)); + @NonNull + public final Completable compose(@NonNull CompletableTransformer transformer) { + return wrap(Objects.requireNonNull(transformer, "transformer is null").apply(this)); } /** - * Concatenates this Completable with another Completable. + * Concatenates this {@code Completable} with another {@link CompletableSource}. + * An error event from this {@code Completable} will be + * propagated to the downstream observer and will result in skipping the subscription to the next + * {@code CompletableSource}. *

* *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param other the other Completable, not null - * @return the new Completable which subscribes to this and then the other Completable - * @throws NullPointerException if other is null + * @param other the other {@code CompletableSource}, not {@code null} + * @return the new {@code Completable} which subscribes to this and then the other {@code CompletableSource} + * @throws NullPointerException if {@code other} is {@code null} + * @see #andThen(CompletableSource) * @see #andThen(MaybeSource) * @see #andThen(ObservableSource) * @see #andThen(SingleSource) @@ -1334,78 +1665,80 @@ public final Completable compose(CompletableTransformer transformer) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable concatWith(CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Completable concatWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); return RxJavaPlugins.onAssembly(new CompletableAndThenCompletable(this, other)); } /** - * Returns a Completable which delays the emission of the completion event by the given time. + * Returns a {@code Completable} which delays the emission of the completion event by the given time. *

- * + * *

*
Scheduler:
*
{@code delay} does operate by default on the {@code computation} {@link Scheduler}.
*
- * @param delay the delay time + * @param time the delay time * @param unit the delay unit - * @return the new Completable instance - * @throws NullPointerException if unit is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Completable delay(long delay, TimeUnit unit) { - return delay(delay, unit, Schedulers.computation(), false); + @NonNull + public final Completable delay(long time, @NonNull TimeUnit unit) { + return delay(time, unit, Schedulers.computation(), false); } /** - * Returns a Completable which delays the emission of the completion event by the given time while - * running on the specified scheduler. + * Returns a {@code Completable} which delays the emission of the completion event by the given time while + * running on the specified {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code delay} operates on the {@link Scheduler} you specify.
+ *
{@code delay} operates on the {@code Scheduler} you specify.
*
- * @param delay the delay time + * @param time the delay time * @param unit the delay unit - * @param scheduler the scheduler to run the delayed completion on - * @return the new Completable instance - * @throws NullPointerException if unit or scheduler is null + * @param scheduler the {@code Scheduler} to run the delayed completion on + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable delay(long delay, TimeUnit unit, Scheduler scheduler) { - return delay(delay, unit, scheduler, false); + @NonNull + public final Completable delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delay(time, unit, scheduler, false); } /** - * Returns a Completable which delays the emission of the completion event, and optionally the error as well, by the given time while - * running on the specified scheduler. + * Returns a {@code Completable} which delays the emission of the completion event, and optionally the error as well, by the given time while + * running on the specified {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code delay} operates on the {@link Scheduler} you specify.
+ *
{@code delay} operates on the {@code Scheduler} you specify.
*
- * @param delay the delay time + * @param time the delay time * @param unit the delay unit - * @param scheduler the scheduler to run the delayed completion on + * @param scheduler the {@code Scheduler} to run the delayed completion on * @param delayError delay the error emission as well? - * @return the new Completable instance - * @throws NullPointerException if unit or scheduler is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable delay(final long delay, final TimeUnit unit, final Scheduler scheduler, final boolean delayError) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new CompletableDelay(this, delay, unit, scheduler, delayError)); + public final Completable delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new CompletableDelay(this, time, unit, scheduler, delayError)); } /** - * Returns a Completable that delays the subscription to the source CompletableSource by a given amount of time. + * Returns a {@code Completable} that delays the subscription to the upstream by a given amount of time. *

* *

@@ -1414,139 +1747,177 @@ public final Completable delay(final long delay, final TimeUnit unit, final Sche *
*

History: 2.2.3 - experimental * - * @param delay the time to delay the subscription + * @param time the time to delay the subscription * @param unit the time unit of {@code delay} - * @return a Completable that delays the subscription to the source CompletableSource by the given amount + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @since 3.0.0 * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Completable delaySubscription(long delay, TimeUnit unit) { - return delaySubscription(delay, unit, Schedulers.computation()); + @NonNull + public final Completable delaySubscription(long time, @NonNull TimeUnit unit) { + return delaySubscription(time, unit, Schedulers.computation()); } /** - * Returns a Completable that delays the subscription to the source CompletableSource by a given amount of time, - * both waiting and subscribing on a given Scheduler. + * Returns a {@code Completable} that delays the subscription to the upstream by a given amount of time, + * both waiting and subscribing on a given {@link Scheduler}. *

* *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
*

History: 2.2.3 - experimental - * @param delay the time to delay the subscription + * @param time the time to delay the subscription * @param unit the time unit of {@code delay} - * @param scheduler the Scheduler on which the waiting and subscription will happen - * @return a Completable that delays the subscription to the source CompletableSource by a given - * amount, waiting and subscribing on the given Scheduler + * @param scheduler the {@code Scheduler} on which the waiting and subscription will happen + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 3.0.0 * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable delaySubscription(long delay, TimeUnit unit, Scheduler scheduler) { - return Completable.timer(delay, unit, scheduler).andThen(this); + @NonNull + public final Completable delaySubscription(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return Completable.timer(time, unit, scheduler).andThen(this); } /** - * Returns a Completable which calls the given onComplete callback if this Completable completes. + * Returns a {@code Completable} which calls the given {@code onComplete} {@link Action} if this {@code Completable} completes. *

* *

*
Scheduler:
*
{@code doOnComplete} does not operate by default on a particular {@link Scheduler}.
*
- * @param onComplete the callback to call when this emits an onComplete event - * @return the new Completable instance - * @throws NullPointerException if onComplete is null + * @param onComplete the {@code Action} to call when this emits an {@code onComplete} event + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onComplete} is {@code null} * @see #doFinally(Action) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doOnComplete(Action onComplete) { + @NonNull + public final Completable doOnComplete(@NonNull Action onComplete) { return doOnLifecycle(Functions.emptyConsumer(), Functions.emptyConsumer(), onComplete, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Calls the shared {@code Action} if a CompletableObserver subscribed to the current - * Completable disposes the common Disposable it received via onSubscribe. + * Calls the shared {@link Action} if a {@link CompletableObserver} subscribed to the current + * {@code Completable} disposes the common {@link Disposable} it received via {@code onSubscribe}. *

* *

*
Scheduler:
*
{@code doOnDispose} does not operate by default on a particular {@link Scheduler}.
*
- * @param onDispose the action to call when the child subscriber disposes the subscription - * @return the new Completable instance - * @throws NullPointerException if onDispose is null + * @param onDispose the {@code Action} to call when the downstream observer disposes the subscription + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onDispose} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doOnDispose(Action onDispose) { + @NonNull + public final Completable doOnDispose(@NonNull Action onDispose) { return doOnLifecycle(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, onDispose); } /** - * Returns a Completable which calls the given onError callback if this Completable emits an error. + * Returns a {@code Completable} which calls the given {@code onError} {@link Consumer} if this {@code Completable} emits an error. *

* *

*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
*
- * @param onError the error callback - * @return the new Completable instance - * @throws NullPointerException if onError is null + * @param onError the error {@code Consumer} receiving the upstream {@link Throwable} if the upstream signals it via {@code onError} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onError} is {@code null} * @see #doFinally(Action) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doOnError(Consumer onError) { + @NonNull + public final Completable doOnError(@NonNull Consumer onError) { return doOnLifecycle(Functions.emptyConsumer(), onError, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Returns a Completable which calls the given onEvent callback with the (throwable) for an onError - * or (null) for an onComplete signal from this Completable before delivering said signal to the downstream. + * Returns a {@code Completable} which calls the given {@code onEvent} {@link Consumer} with the {@link Throwable} for an {@code onError} + * or {@code null} for an {@code onComplete} signal from this {@code Completable} before delivering the signal to the downstream. *

* *

*
Scheduler:
*
{@code doOnEvent} does not operate by default on a particular {@link Scheduler}.
*
- * @param onEvent the event callback - * @return the new Completable instance - * @throws NullPointerException if onEvent is null + * @param onEvent the event {@code Consumer} that receives {@code null} for upstream + * completion or a {@code Throwable} if the upstream signaled an error + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onEvent} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doOnEvent(final Consumer onEvent) { - ObjectHelper.requireNonNull(onEvent, "onEvent is null"); + public final Completable doOnEvent(@NonNull Consumer<@Nullable ? super Throwable> onEvent) { + Objects.requireNonNull(onEvent, "onEvent is null"); return RxJavaPlugins.onAssembly(new CompletableDoOnEvent(this, onEvent)); } /** - * Returns a Completable instance that calls the various callbacks on the specific + * Calls the appropriate {@code onXXX} method (shared between all {@link CompletableObserver}s) for the lifecycle events of + * the sequence (subscription, disposal). + *

+ * + *

+ *
Scheduler:
+ *
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param onSubscribe + * a {@link Consumer} called with the {@link Disposable} sent via {@link CompletableObserver#onSubscribe(Disposable)} + * @param onDispose + * called when the downstream disposes the {@code Disposable} via {@code dispose()} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onSubscribe} or {@code onDispose} is {@code null} + * @see ReactiveX operators documentation: Do + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Completable doOnLifecycle(@NonNull Consumer onSubscribe, @NonNull Action onDispose) { + return doOnLifecycle(onSubscribe, Functions.emptyConsumer(), + Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, + Functions.EMPTY_ACTION, onDispose); + } + + /** + * Returns a {@code Completable} instance that calls the various callbacks upon the specific * lifecycle events. *
*
Scheduler:
*
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
*
- * @param onSubscribe the consumer called when a CompletableSubscriber subscribes. - * @param onError the consumer called when this emits an onError event - * @param onComplete the runnable called just before when this Completable completes normally - * @param onAfterTerminate the runnable called after this Completable completes normally - * @param onDispose the runnable called when the child disposes the subscription - * @return the new Completable instance + * @param onSubscribe the {@link Consumer} called when a {@link CompletableObserver} subscribes. + * @param onError the {@code Consumer} called when this emits an {@code onError} event + * @param onComplete the {@link Action} called just before when the current {@code Completable} completes normally + * @param onTerminate the {@code Action} called just before this {@code Completable} terminates + * @param onAfterTerminate the {@code Action} called after this {@code Completable} completes normally + * @param onDispose the {@code Action} called when the downstream disposes the subscription + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onSubscribe}, {@code onError}, {@code onComplete} + * {@code onTerminate}, {@code onAfterTerminate} or {@code onDispose} is {@code null} */ @CheckReturnValue @NonNull @@ -1558,73 +1929,78 @@ private Completable doOnLifecycle( final Action onTerminate, final Action onAfterTerminate, final Action onDispose) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - ObjectHelper.requireNonNull(onTerminate, "onTerminate is null"); - ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); - ObjectHelper.requireNonNull(onDispose, "onDispose is null"); + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onTerminate, "onTerminate is null"); + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); + Objects.requireNonNull(onDispose, "onDispose is null"); return RxJavaPlugins.onAssembly(new CompletablePeek(this, onSubscribe, onError, onComplete, onTerminate, onAfterTerminate, onDispose)); } /** - * Returns a Completable instance that calls the given onSubscribe callback with the disposable - * that child subscribers receive on subscription. + * Returns a {@code Completable} instance that calls the given {@code onSubscribe} callback with the disposable + * that the downstream {@link CompletableObserver}s receive upon subscription. *

* *

*
Scheduler:
*
{@code doOnSubscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param onSubscribe the callback called when a child subscriber subscribes - * @return the new Completable instance - * @throws NullPointerException if onSubscribe is null + * @param onSubscribe the {@link Consumer} called when a downstream {@code CompletableObserver} subscribes + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doOnSubscribe(Consumer onSubscribe) { + @NonNull + public final Completable doOnSubscribe(@NonNull Consumer onSubscribe) { return doOnLifecycle(onSubscribe, Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Returns a Completable instance that calls the given onTerminate callback just before this Completable + * Returns a {@code Completable} instance that calls the given {@code onTerminate} {@link Action} just before this {@code Completable} * completes normally or with an exception. *

- * + * *

*
Scheduler:
*
{@code doOnTerminate} does not operate by default on a particular {@link Scheduler}.
*
- * @param onTerminate the callback to call just before this Completable terminates - * @return the new Completable instance + * @param onTerminate the {@code Action} to call just before this {@code Completable} terminates + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onTerminate} is {@code null} * @see #doFinally(Action) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doOnTerminate(final Action onTerminate) { + @NonNull + public final Completable doOnTerminate(@NonNull Action onTerminate) { return doOnLifecycle(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, onTerminate, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Returns a Completable instance that calls the given onTerminate callback after this Completable + * Returns a {@code Completable} instance that calls the given {@code onAfterTerminate} {@link Action} after this {@code Completable} * completes normally or with an exception. *

- * + * *

*
Scheduler:
*
{@code doAfterTerminate} does not operate by default on a particular {@link Scheduler}.
*
- * @param onAfterTerminate the callback to call after this Completable terminates - * @return the new Completable instance + * @param onAfterTerminate the {@code Action} to call after this {@code Completable} terminates + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onAfterTerminate} is {@code null} * @see #doFinally(Action) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doAfterTerminate(final Action onAfterTerminate) { + @NonNull + public final Completable doAfterTerminate(@NonNull Action onAfterTerminate) { return doOnLifecycle( Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -1634,7 +2010,7 @@ public final Completable doAfterTerminate(final Action onAfterTerminate) { Functions.EMPTY_ACTION); } /** - * Calls the specified action after this Completable signals onError or onComplete or gets disposed by + * Calls the specified {@link Action} after this {@code Completable} signals {@code onError} or {@code onComplete} or gets disposed by * the downstream. *

* @@ -1649,15 +2025,16 @@ public final Completable doAfterTerminate(final Action onAfterTerminate) { *

{@code doFinally} does not operate by default on a particular {@link Scheduler}.
* *

History: 2.0.1 - experimental - * @param onFinally the action called when this Completable terminates or gets disposed - * @return the new Completable instance + * @param onFinally the {@code Action} called when this {@code Completable} terminates or gets disposed + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onFinally} is {@code null} * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable doFinally(Action onFinally) { - ObjectHelper.requireNonNull(onFinally, "onFinally is null"); + public final Completable doFinally(@NonNull Action onFinally) { + Objects.requireNonNull(onFinally, "onFinally is null"); return RxJavaPlugins.onAssembly(new CompletableDoFinally(this, onFinally)); } @@ -1772,56 +2149,58 @@ public final Completable doFinally(Action onFinally) { * class and creating a {@link CompletableTransformer} with it is recommended. *

* Note also that it is not possible to stop the subscription phase in {@code lift()} as the {@code apply()} method - * requires a non-null {@code CompletableObserver} instance to be returned, which is then unconditionally subscribed to - * the upstream {@code Completable}. For example, if the operator decided there is no reason to subscribe to the + * requires a non-{@code null} {@code CompletableObserver} instance to be returned, which is then unconditionally subscribed to + * the current {@code Completable}. For example, if the operator decided there is no reason to subscribe to the * upstream source because of some optimization possibility or a failure to prepare the operator, it still has to - * return a {@code CompletableObserver} that should immediately dispose the upstream's {@code Disposable} in its + * return a {@code CompletableObserver} that should immediately dispose the upstream's {@link Disposable} in its * {@code onSubscribe} method. Again, using a {@code CompletableTransformer} and extending the {@code Completable} is * a better option as {@link #subscribeActual} can decide to not subscribe to its upstream after all. *

*
Scheduler:
*
{@code lift} does not operate by default on a particular {@link Scheduler}, however, the - * {@link CompletableOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
+ * {@code CompletableOperator} may use a {@code Scheduler} to support its own asynchronous behavior. *
* - * @param onLift the {@link CompletableOperator} that receives the downstream's {@code CompletableObserver} and should return + * @param onLift the {@code CompletableOperator} that receives the downstream's {@code CompletableObserver} and should return * a {@code CompletableObserver} with custom behavior to be used as the consumer for the current * {@code Completable}. - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code onLift} is {@code null} * @see RxJava wiki: Writing operators * @see #compose(CompletableTransformer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable lift(final CompletableOperator onLift) { - ObjectHelper.requireNonNull(onLift, "onLift is null"); + public final Completable lift(@NonNull CompletableOperator onLift) { + Objects.requireNonNull(onLift, "onLift is null"); return RxJavaPlugins.onAssembly(new CompletableLift(this, onLift)); } /** - * Maps the signal types of this Completable into a {@link Notification} of the same kind + * Maps the signal types of this {@code Completable} into a {@link Notification} of the same kind * and emits it as a single success value to downstream. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.2.4 - experimental - * @param the intended target element type of the notification - * @return the new Single instance + * @param the intended target element type of the {@code Notification} + * @return the new {@link Single} instance * @since 3.0.0 * @see Single#dematerialize(Function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> materialize() { - return RxJavaPlugins.onAssembly(new CompletableMaterialize(this)); + @NonNull + public final <@NonNull T> Single> materialize() { + return RxJavaPlugins.onAssembly(new CompletableMaterialize<>(this)); } /** - * Returns a Completable which subscribes to this and the other Completable and completes + * Returns a {@code Completable} which subscribes to this and the other {@link CompletableSource} and completes * when both of them complete or one emits an error. *

* @@ -1829,80 +2208,82 @@ public final Single> materialize() { *

Scheduler:
*
{@code mergeWith} does not operate by default on a particular {@link Scheduler}.
* - * @param other the other Completable instance - * @return the new Completable instance - * @throws NullPointerException if other is null + * @param other the other {@code CompletableSource} instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code other} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable mergeWith(CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Completable mergeWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); return mergeArray(this, other); } /** - * Returns a Completable which emits the terminal events from the thread of the specified scheduler. + * Returns a {@code Completable} which emits the terminal events from the thread of the specified {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code observeOn} operates on a {@link Scheduler} you specify.
+ *
{@code observeOn} operates on a {@code Scheduler} you specify.
*
- * @param scheduler the scheduler to emit terminal events on - * @return the new Completable instance - * @throws NullPointerException if scheduler is null + * @param scheduler the {@code Scheduler} to emit terminal events on + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable observeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Completable observeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new CompletableObserveOn(this, scheduler)); } /** - * Returns a Completable instance that if this Completable emits an error, it will emit an onComplete - * and swallow the throwable. + * Returns a {@code Completable} instance that if this {@code Completable} emits an error, it will emit an {@code onComplete} + * and swallow the upstream {@link Throwable}. *

* *

*
Scheduler:
*
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Completable instance + * @return the new {@code Completable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable onErrorComplete() { return onErrorComplete(Functions.alwaysTrue()); } /** - * Returns a Completable instance that if this Completable emits an error and the predicate returns - * true, it will emit an onComplete and swallow the throwable. + * Returns a {@code Completable} instance that if this {@code Completable} emits an error and the {@link Predicate} returns + * {@code true}, it will emit an {@code onComplete} and swallow the {@link Throwable}. *

* *

*
Scheduler:
*
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
*
- * @param predicate the predicate to call when an Throwable is emitted which should return true - * if the Throwable should be swallowed and replaced with an onComplete. - * @return the new Completable instance + * @param predicate the {@code Predicate} to call when a {@code Throwable} is emitted which should return {@code true} + * if the {@code Throwable} should be swallowed and replaced with an {@code onComplete}. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable onErrorComplete(final Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); + public final Completable onErrorComplete(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); return RxJavaPlugins.onAssembly(new CompletableOnErrorComplete(this, predicate)); } /** - * Returns a Completable instance that when encounters an error from this Completable, calls the - * specified mapper function that returns another Completable instance for it and resumes the + * Returns a {@code Completable} instance that when encounters an error from this {@code Completable}, calls the + * specified {@code mapper} {@link Function} that returns a {@link CompletableSource} instance for it and resumes the * execution with it. *

* @@ -1910,21 +2291,108 @@ public final Completable onErrorComplete(final Predicate pred *

Scheduler:
*
{@code onErrorResumeNext} does not operate by default on a particular {@link Scheduler}.
* - * @param errorMapper the mapper function that takes the error and should return a Completable as + * @param fallbackSupplier the {@code mapper} {@code Function} that takes the error and should return a {@code CompletableSource} as * continuation. - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code fallbackSupplier} is {@code null} + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Completable onErrorResumeNext(@NonNull Function fallbackSupplier) { + Objects.requireNonNull(fallbackSupplier, "fallbackSupplier is null"); + return RxJavaPlugins.onAssembly(new CompletableResumeNext(this, fallbackSupplier)); + } + /** + * Resumes the flow with the given {@link CompletableSource} when the current {@code Completable} fails instead of + * signaling the error via {@code onError}. + *

+ * + *

+ * You can use this to prevent errors from propagating or to supply fallback data should errors be + * encountered. + *

+ *
Scheduler:
+ *
{@code onErrorResumeWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param fallback + * the next {@code CompletableSource} that will take over if the current {@code Completable} encounters + * an error + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code fallback} is {@code null} + * @see ReactiveX operators documentation: Catch + * @since 3.0.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable onErrorResumeNext(final Function errorMapper) { - ObjectHelper.requireNonNull(errorMapper, "errorMapper is null"); - return RxJavaPlugins.onAssembly(new CompletableResumeNext(this, errorMapper)); + public final Completable onErrorResumeWith(@NonNull CompletableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return onErrorResumeNext(Functions.justFunction(fallback)); } /** - * Nulls out references to the upstream producer and downstream CompletableObserver if - * the sequence is terminated or downstream calls dispose(). + * Ends the flow with a success item returned by a function for the {@link Throwable} error signaled by the current + * {@code Completable} instead of signaling the error via {@code onError}. + *

+ * + *

+ * You can use this to prevent errors from propagating or to supply fallback data should errors be + * encountered. + *

+ *
Scheduler:
+ *
{@code onErrorReturn} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the item type to return on error + * @param itemSupplier + * a function that returns a single value that will be emitted as success value + * the current {@code Completable} signals an {@code onError} event + * @return the new {@link Maybe} instance + * @throws NullPointerException if {@code itemSupplier} is {@code null} + * @see ReactiveX operators documentation: Catch + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull T> Maybe onErrorReturn(@NonNull Function itemSupplier) { + Objects.requireNonNull(itemSupplier, "itemSupplier is null"); + return RxJavaPlugins.onAssembly(new CompletableOnErrorReturn<>(this, itemSupplier)); + } + + /** + * Ends the flow with the given success item when the current {@code Completable} + * fails instead of signaling the error via {@code onError}. + *

+ * + *

+ * You can use this to prevent errors from propagating or to supply fallback data should errors be + * encountered. + *

+ *
Scheduler:
+ *
{@code onErrorReturnItem} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the item type to return on error + * @param item + * the value that is emitted as {@code onSuccess} in case the current {@code Completable} signals an {@code onError} + * @return the new {@link Maybe} instance + * @throws NullPointerException if {@code item} is {@code null} + * @see ReactiveX operators documentation: Catch + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull T> Maybe onErrorReturnItem(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); + return onErrorReturn(Functions.justFunction(item)); + } + + /** + * Nulls out references to the upstream producer and downstream {@link CompletableObserver} if + * the sequence is terminated or downstream calls {@code dispose()}. *

* *

@@ -1932,127 +2400,134 @@ public final Completable onErrorResumeNext(final Function{@code onTerminateDetach} does not operate by default on a particular {@link Scheduler}. *
*

History: 2.1.5 - experimental - * @return a Completable which nulls out references to the upstream producer and downstream CompletableObserver if - * the sequence is terminated or downstream calls dispose() + * @return the new {@code Completable} instance * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable onTerminateDetach() { return RxJavaPlugins.onAssembly(new CompletableDetach(this)); } /** - * Returns a Completable that repeatedly subscribes to this Completable until disposed. + * Returns a {@code Completable} that repeatedly subscribes to this {@code Completable} until disposed. *

* *

*
Scheduler:
*
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Completable instance + * @return the new {@code Completable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable repeat() { return fromPublisher(toFlowable().repeat()); } /** - * Returns a Completable that subscribes repeatedly at most the given times to this Completable. + * Returns a {@code Completable} that subscribes repeatedly at most the given number of times to this {@code Completable}. *

* *

*
Scheduler:
*
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
- * @param times the number of times the resubscription should happen - * @return the new Completable instance - * @throws IllegalArgumentException if times is less than zero + * @param times the number of times the re-subscription should happen + * @return the new {@code Completable} instance + * @throws IllegalArgumentException if {@code times} is negative */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable repeat(long times) { return fromPublisher(toFlowable().repeat(times)); } /** - * Returns a Completable that repeatedly subscribes to this Completable so long as the given - * stop supplier returns false. + * Returns a {@code Completable} that repeatedly subscribes to this {@code Completable} so long as the given + * stop {@link BooleanSupplier} returns {@code false}. *

* *

*
Scheduler:
*
{@code repeatUntil} does not operate by default on a particular {@link Scheduler}.
*
- * @param stop the supplier that should return true to stop resubscribing. - * @return the new Completable instance - * @throws NullPointerException if stop is null + * @param stop the {@code BooleanSupplier} that should return {@code true} to stop resubscribing. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code stop} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable repeatUntil(BooleanSupplier stop) { + @NonNull + public final Completable repeatUntil(@NonNull BooleanSupplier stop) { return fromPublisher(toFlowable().repeatUntil(stop)); } /** - * Returns a Completable instance that repeats when the Publisher returned by the handler - * emits an item or completes when this Publisher emits a completed event. + * Returns a {@code Completable} instance that repeats when the {@link Publisher} returned by the handler {@link Function} + * emits an item or completes when this {@code Publisher} emits an {@code onComplete} event. *

* *

*
Scheduler:
*
{@code repeatWhen} does not operate by default on a particular {@link Scheduler}.
*
- * @param handler the function that transforms the stream of values indicating the completion of - * this Completable and returns a Publisher that emits items for repeating or completes to indicate the + * @param handler the {@code Function} that transforms the stream of values indicating the completion of + * this {@code Completable} and returns a {@code Publisher} that emits items for repeating or completes to indicate the * repetition should stop - * @return the new Completable instance - * @throws NullPointerException if stop is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code handler} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable repeatWhen(Function, ? extends Publisher> handler) { + @NonNull + public final Completable repeatWhen(@NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { return fromPublisher(toFlowable().repeatWhen(handler)); } /** - * Returns a Completable that retries this Completable as long as it emits an onError event. + * Returns a {@code Completable} that retries this {@code Completable} as long as it emits an {@code onError} event. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Completable instance + * @return the new {@code Completable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable retry() { return fromPublisher(toFlowable().retry()); } /** - * Returns a Completable that retries this Completable in case of an error as long as the predicate - * returns true. + * Returns a {@code Completable} that retries this {@code Completable} in case of an error as long as the {@code predicate} + * returns {@code true}. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param predicate the predicate called when this emits an error with the repeat count and the latest exception - * and should return true to retry. - * @return the new Completable instance + * @param predicate the {@link Predicate} called when this {@code Completable} emits an error with the repeat count and the latest {@link Throwable} + * and should return {@code true} to retry. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable retry(BiPredicate predicate) { + @NonNull + public final Completable retry(@NonNull BiPredicate predicate) { return fromPublisher(toFlowable().retry(predicate)); } /** - * Returns a Completable that when this Completable emits an error, retries at most the given + * Returns a {@code Completable} that when this {@code Completable} emits an error, retries at most the given * number of times before giving up and emitting the last error. *

* @@ -2060,19 +2535,20 @@ public final Completable retry(BiPredicate p *

Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
* - * @param times the number of times to resubscribe if the current Completable fails - * @return the new Completable instance - * @throws IllegalArgumentException if times is negative + * @param times the number of times to resubscribe if the current {@code Completable} fails + * @return the new {@code Completable} instance + * @throws IllegalArgumentException if {@code times} is negative */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable retry(long times) { return fromPublisher(toFlowable().retry(times)); } /** - * Returns a Completable that when this Completable emits an error, retries at most times - * or until the predicate returns false, whichever happens first and emitting the last error. + * Returns a {@code Completable} that when this {@code Completable} emits an error, retries at most times + * or until the predicate returns {@code false}, whichever happens first and emitting the last error. *

* *

@@ -2080,51 +2556,74 @@ public final Completable retry(long times) { *
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.8 - experimental - * @param times the number of times to resubscribe if the current Completable fails - * @param predicate the predicate that is called with the latest throwable and should return - * true to indicate the returned Completable should resubscribe to this Completable. - * @return the new Completable instance - * @throws NullPointerException if predicate is null - * @throws IllegalArgumentException if times is negative + * @param times the number of times to resubscribe if the current {@code Completable} fails + * @param predicate the {@link Predicate} that is called with the latest {@link Throwable} and should return + * {@code true} to indicate the returned {@code Completable} should resubscribe to this {@code Completable}. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code predicate} is {@code null} + * @throws IllegalArgumentException if {@code times} is negative * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable retry(long times, Predicate predicate) { + @NonNull + public final Completable retry(long times, @NonNull Predicate predicate) { return fromPublisher(toFlowable().retry(times, predicate)); } /** - * Returns a Completable that when this Completable emits an error, calls the given predicate with - * the latest exception to decide whether to resubscribe to this or not. + * Returns a {@code Completable} that when this {@code Completable} emits an error, calls the given predicate with + * the latest {@link Throwable} to decide whether to resubscribe to the upstream or not. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param predicate the predicate that is called with the latest throwable and should return - * true to indicate the returned Completable should resubscribe to this Completable. - * @return the new Completable instance - * @throws NullPointerException if predicate is null + * @param predicate the {@link Predicate} that is called with the latest {@code Throwable} and should return + * {@code true} to indicate the returned {@code Completable} should resubscribe to this {@code Completable}. + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable retry(Predicate predicate) { + @NonNull + public final Completable retry(@NonNull Predicate predicate) { return fromPublisher(toFlowable().retry(predicate)); } /** - * Returns a Completable which given a Publisher and when this Completable emits an error, delivers - * that error through a Flowable and the Publisher should signal a value indicating a retry in response + * Retries until the given stop function returns {@code true}. + *

+ * + *

+ *
Scheduler:
+ *
{@code retryUntil} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param stop the function that should return {@code true} to stop retrying + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code stop} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Completable retryUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); + return retry(Long.MAX_VALUE, Functions.predicateReverseFor(stop)); + } + + /** + * Returns a {@code Completable} which given a {@link Publisher} and when this {@code Completable} emits an error, delivers + * that error through a {@link Flowable} and the {@code Publisher} should signal a value indicating a retry in response * or a terminal event indicating a termination. *

* *

* Note that the inner {@code Publisher} returned by the handler function should signal * either {@code onNext}, {@code onError} or {@code onComplete} in response to the received - * {@code Throwable} to indicate the operator should retry or terminate. If the upstream to - * the operator is asynchronous, signalling onNext followed by onComplete immediately may + * {@link Throwable} to indicate the operator should retry or terminate. If the upstream to + * the operator is asynchronous, signaling {@code onNext} followed by {@code onComplete} immediately may * result in the sequence to be completed immediately. Similarly, if this inner * {@code Publisher} signals {@code onError} or {@code onComplete} while the upstream is * active, the sequence is terminated with the same signal immediately. @@ -2149,41 +2648,119 @@ public final Completable retry(Predicate predicate) { *

Scheduler:
*
{@code retryWhen} does not operate by default on a particular {@link Scheduler}.
* - * @param handler the handler that receives a Flowable delivering Throwables and should return a Publisher that + * @param handler the {@link Function} that receives a {@code Flowable} delivering {@code Throwable}s and should return a {@code Publisher} that * emits items to indicate retries or emits terminal events to indicate termination. - * @return the new Completable instance - * @throws NullPointerException if handler is null + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code handler} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable retryWhen(Function, ? extends Publisher> handler) { + @NonNull + public final Completable retryWhen(@NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { return fromPublisher(toFlowable().retryWhen(handler)); } /** - * Returns a Completable which first runs the other Completable - * then this completable if the other completed normally. + * Wraps the given {@link CompletableObserver}, catches any {@link RuntimeException}s thrown by its + * {@link CompletableObserver#onSubscribe(Disposable)}, {@link CompletableObserver#onError(Throwable)} + * or {@link CompletableObserver#onComplete()} methods and routes those to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *

+ * By default, the {@code Completable} protocol forbids the {@code onXXX} methods to throw, but some + * {@code CompletableObserver} implementation may do it anyway, causing undefined behavior in the + * upstream. This method and the underlying safe wrapper ensures such misbehaving consumers don't + * disrupt the protocol. + *

+ *
Scheduler:
+ *
{@code safeSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param observer the potentially misbehaving {@code CompletableObserver} + * @throws NullPointerException if {@code observer} is {@code null} + * @see #subscribe(Action, Consumer) + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void safeSubscribe(@NonNull CompletableObserver observer) { + Objects.requireNonNull(observer, "observer is null"); + subscribe(new SafeCompletableObserver(observer)); + } + + /** + * Returns a {@code Completable} which first runs the other {@link CompletableSource} + * then the current {@code Completable} if the other completed normally. *

* *

*
Scheduler:
*
{@code startWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param other the other completable to run first - * @return the new Completable instance - * @throws NullPointerException if other is null + * @param other the other {@code CompletableSource} to run first + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code other} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable startWith(CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Completable startWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); return concatArray(other, this); } /** - * Returns an Observable which first delivers the events - * of the other Observable then runs this CompletableConsumable. + * Returns a {@link Flowable} which first runs the other {@link SingleSource} + * then the current {@code Completable} if the other succeeded normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code other} {@code SingleSource}. + * @param other the other {@code SingleSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final <@NonNull T> Flowable startWith(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Single.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link MaybeSource} + * then the current {@code Completable} if the other succeeded or completed normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code other} {@code MaybeSource}. + * @param other the other {@code MaybeSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final <@NonNull T> Flowable startWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Maybe.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns an {@link Observable} which first delivers the events + * of the other {@link ObservableSource} then runs the current {@code Completable}. *

* *

@@ -2191,20 +2768,21 @@ public final Completable startWith(CompletableSource other) { *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param other the other Observable to run first - * @return the new Observable instance - * @throws NullPointerException if other is null + * @param other the other {@code ObservableSource} to run first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Observable startWith(Observable other) { - ObjectHelper.requireNonNull(other, "other is null"); - return other.concatWith(this.toObservable()); + public final <@NonNull T> Observable startWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); + return Observable.wrap(other).concatWith(this.toObservable()); } + /** - * Returns a Flowable which first delivers the events - * of the other Publisher then runs this Completable. + * Returns a {@link Flowable} which first delivers the events + * of the other {@link Publisher} then runs the current {@code Completable}. *

* *

@@ -2215,21 +2793,21 @@ public final Observable startWith(Observable other) { *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param other the other Publisher to run first - * @return the new Flowable instance - * @throws NullPointerException if other is null + * @param other the other {@code Publisher} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable startWith(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final <@NonNull T> Flowable startWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); return this.toFlowable().startWith(other); } /** - * Hides the identity of this Completable and its Disposable. + * Hides the identity of this {@code Completable} and its {@link Disposable}. *

* *

@@ -2239,17 +2817,18 @@ public final Flowable startWith(Publisher other) { *

{@code hide} does not operate by default on a particular {@link Scheduler}.
* *

History: 2.0.5 - experimental - * @return the new Completable instance + * @return the new {@code Completable} instance * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable hide() { return RxJavaPlugins.onAssembly(new CompletableHide(this)); } /** - * Subscribes to this CompletableConsumable and returns a Disposable which can be used to dispose + * Subscribes to this {@code Completable} and returns a {@link Disposable} which can be used to dispose * the subscription. *

* @@ -2257,9 +2836,11 @@ public final Completable hide() { *

Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
* - * @return the Disposable that allows disposing the subscription + * @return the new {@code Disposable} that can be used for disposing the subscription at any time + * @see #subscribe(Action, Consumer, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Disposable subscribe() { EmptyCompletableObserver observer = new EmptyCompletableObserver(); subscribe(observer); @@ -2268,13 +2849,13 @@ public final Disposable subscribe() { @SchedulerSupport(SchedulerSupport.NONE) @Override - public final void subscribe(CompletableObserver observer) { - ObjectHelper.requireNonNull(observer, "observer is null"); + public final void subscribe(@NonNull CompletableObserver observer) { + Objects.requireNonNull(observer, "observer is null"); try { observer = RxJavaPlugins.onSubscribe(this, observer); - ObjectHelper.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null CompletableObserver. Please check the handler provided to RxJavaPlugins.setOnCompletableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); + Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null CompletableObserver. Please check the handler provided to RxJavaPlugins.setOnCompletableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); subscribeActual(observer); } catch (NullPointerException ex) { // NOPMD @@ -2292,13 +2873,13 @@ public final void subscribe(CompletableObserver observer) { *

There is no need to call any of the plugin hooks on the current {@code Completable} instance or * the {@code CompletableObserver}; all hooks and basic safeguards have been * applied by {@link #subscribe(CompletableObserver)} before this method gets called. - * @param observer the CompletableObserver instance, never null + * @param observer the {@code CompletableObserver} instance, never {@code null} */ - protected abstract void subscribeActual(CompletableObserver observer); + protected abstract void subscribeActual(@NonNull CompletableObserver observer); /** - * Subscribes a given CompletableObserver (subclass) to this Completable and returns the given - * CompletableObserver as is. + * Subscribes a given {@link CompletableObserver} (subclass) to this {@code Completable} and returns the given + * {@code CompletableObserver} as is. *

* *

Usage example: @@ -2316,38 +2897,40 @@ public final void subscribe(CompletableObserver observer) { *

Scheduler:
*
{@code subscribeWith} does not operate by default on a particular {@link Scheduler}.
* - * @param the type of the CompletableObserver to use and return - * @param observer the CompletableObserver (subclass) to use and return, not null + * @param the type of the {@code CompletableObserver} to use and return + * @param observer the {@code CompletableObserver} (subclass) to use and return, not {@code null} * @return the input {@code observer} - * @throws NullPointerException if {@code observer} is null + * @throws NullPointerException if {@code observer} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final E subscribeWith(E observer) { + @NonNull + public final <@NonNull E extends CompletableObserver> E subscribeWith(E observer) { subscribe(observer); return observer; } /** - * Subscribes to this Completable and calls back either the onError or onComplete functions. + * Subscribes to this {@code Completable} and calls back either the {@code onError} or {@code onComplete} functions. *

* *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param onComplete the runnable that is called if the Completable completes normally - * @param onError the consumer that is called if this Completable emits an error - * @return the Disposable that can be used for disposing the subscription asynchronously - * @throws NullPointerException if either callback is null + * @param onComplete the {@link Action} that is called if the {@code Completable} completes normally + * @param onError the {@link Consumer} that is called if this {@code Completable} emits an error + * @return the new {@link Disposable} that can be used for disposing the subscription at any time + * @throws NullPointerException if {@code onComplete} or {@code onError} is {@code null} + * @see #subscribe(Action, Consumer, DisposableContainer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(final Action onComplete, final Consumer onError) { - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + public final Disposable subscribe(@NonNull Action onComplete, @NonNull Consumer onError) { + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); CallbackCompletableObserver observer = new CallbackCompletableObserver(onError, onComplete); subscribe(observer); @@ -2355,50 +2938,86 @@ public final Disposable subscribe(final Action onComplete, final Consumer + * The {@code CompletableObserver} will be removed after the callback for the terminal event has been invoked. + *
+ *
Scheduler:
+ *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param onError the callback for an upstream error + * @param onComplete the callback for an upstream completion + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code CompletableObserver} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onComplete}, {@code onError} + * or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Action onComplete, + @NonNull Consumer onError, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseMultiObserver observer = new DisposableAutoReleaseMultiObserver<>( + container, Functions.emptyConsumer(), onError, onComplete); + container.add(observer); + subscribe(observer); + return observer; + } + + /** + * Subscribes to this {@code Completable} and calls the given {@link Action} when this {@code Completable} * completes normally. *

* *

- * If the Completable emits an error, it is wrapped into an - * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * If the {@code Completable} emits an error, it is wrapped into an + * {@link OnErrorNotImplementedException} + * and routed to the global {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param onComplete the runnable called when this Completable completes normally - * @return the Disposable that allows disposing the subscription + * @param onComplete the {@code Action} called when this {@code Completable} completes normally + * @return the new {@link Disposable} that can be used for disposing the subscription at any time + * @throws NullPointerException if {@code onComplete} is {@code null} + * @see #subscribe(Action, Consumer, DisposableContainer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(final Action onComplete) { - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - - CallbackCompletableObserver observer = new CallbackCompletableObserver(onComplete); - subscribe(observer); - return observer; + public final Disposable subscribe(@NonNull Action onComplete) { + return subscribe(onComplete, Functions.ON_ERROR_MISSING); } /** - * Returns a Completable which subscribes the child subscriber on the specified scheduler, making - * sure the subscription side-effects happen on that specific thread of the scheduler. + * Returns a {@code Completable} which subscribes the downstream subscriber on the specified scheduler, making + * sure the subscription side-effects happen on that specific thread of the {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code subscribeOn} operates on a {@link Scheduler} you specify.
+ *
{@code subscribeOn} operates on a {@code Scheduler} you specify.
*
- * @param scheduler the Scheduler to subscribe on - * @return the new Completable instance - * @throws NullPointerException if scheduler is null + * @param scheduler the {@code Scheduler} to subscribe on + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable subscribeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Completable subscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new CompletableSubscribeOn(this, scheduler)); } @@ -2418,137 +3037,140 @@ public final Completable subscribeOn(final Scheduler scheduler) { * *

History: 2.1.17 - experimental * @param other the other completable source to observe for the terminal signals - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable takeUntil(CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Completable takeUntil(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); return RxJavaPlugins.onAssembly(new CompletableTakeUntilCompletable(this, other)); } /** - * Returns a Completable that runs this Completable and emits a TimeoutException in case - * this Completable doesn't complete within the given time. + * Returns a {@code Completabl}e that runs this {@code Completable} and emits a {@link TimeoutException} in case + * this {@code Completable} doesn't complete within the given time. *

* *

*
Scheduler:
- *
{@code timeout} signals the TimeoutException on the {@code computation} {@link Scheduler}.
+ *
{@code timeout} signals the {@code TimeoutException} on the {@code computation} {@link Scheduler}.
*
* @param timeout the timeout value - * @param unit the timeout unit - * @return the new Completable instance - * @throws NullPointerException if unit is null + * @param unit the unit of {@code timeout} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Completable timeout(long timeout, TimeUnit unit) { + @NonNull + public final Completable timeout(long timeout, @NonNull TimeUnit unit) { return timeout0(timeout, unit, Schedulers.computation(), null); } /** - * Returns a Completable that runs this Completable and switches to the other Completable - * in case this Completable doesn't complete within the given time. + * Returns a {@code Completable} that runs this {@code Completable} and switches to the other {@link CompletableSource} + * in case this {@code Completable} doesn't complete within the given time. *

* *

*
Scheduler:
- *
{@code timeout} subscribes to the other CompletableSource on + *
{@code timeout} subscribes to the other {@code CompletableSource} on * the {@code computation} {@link Scheduler}.
*
* @param timeout the timeout value - * @param unit the timeout unit - * @param other the other Completable instance to switch to in case of a timeout - * @return the new Completable instance - * @throws NullPointerException if unit or other is null + * @param unit the unit of {@code timeout} + * @param fallback the other {@code CompletableSource} instance to switch to in case of a timeout + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} or {@code fallback} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Completable timeout(long timeout, TimeUnit unit, CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, unit, Schedulers.computation(), other); + public final Completable timeout(long timeout, @NonNull TimeUnit unit, @NonNull CompletableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, Schedulers.computation(), fallback); } /** - * Returns a Completable that runs this Completable and emits a TimeoutException in case - * this Completable doesn't complete within the given time while "waiting" on the specified - * Scheduler. + * Returns a {@code Completable} that runs this {@code Completable} and emits a {@link TimeoutException} in case + * this {@code Completable} doesn't complete within the given time while "waiting" on the specified + * {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code timeout} signals the TimeoutException on the {@link Scheduler} you specify.
+ *
{@code timeout} signals the {@code TimeoutException} on the {@code Scheduler} you specify.
*
* @param timeout the timeout value - * @param unit the timeout unit - * @param scheduler the scheduler to use to wait for completion - * @return the new Completable instance - * @throws NullPointerException if unit or scheduler is null + * @param unit the unit of {@code timeout} + * @param scheduler the {@code Scheduler} to use to wait for completion and signal {@code TimeoutException} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable timeout(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Completable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return timeout0(timeout, unit, scheduler, null); } /** - * Returns a Completable that runs this Completable and switches to the other Completable - * in case this Completable doesn't complete within the given time while "waiting" on - * the specified scheduler. + * Returns a {@code Completable} that runs this {@code Completable} and switches to the other {@link CompletableSource} + * in case this {@code Completable} doesn't complete within the given time while "waiting" on + * the specified {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code timeout} subscribes to the other CompletableSource on - * the {@link Scheduler} you specify.
+ *
{@code timeout} subscribes to the other {@code CompletableSource} on + * the {@code Scheduler} you specify.
*
* @param timeout the timeout value - * @param unit the timeout unit - * @param scheduler the scheduler to use to wait for completion - * @param other the other Completable instance to switch to in case of a timeout - * @return the new Completable instance - * @throws NullPointerException if unit, scheduler or other is null + * @param unit the unit of {@code timeout} + * @param scheduler the {@code Scheduler} to use to wait for completion + * @param fallback the other {@code Completable} instance to switch to in case of a timeout + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code fallback} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable timeout(long timeout, TimeUnit unit, Scheduler scheduler, CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, unit, scheduler, other); + public final Completable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull CompletableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, scheduler, fallback); } /** - * Returns a Completable that runs this Completable and optionally switches to the other Completable - * in case this Completable doesn't complete within the given time while "waiting" on - * the specified scheduler. + * Returns a {@code Completable} that runs this {@code Completable} and optionally switches to the other {@link CompletableSource} + * in case this {@code Completable} doesn't complete within the given time while "waiting" on + * the specified {@link Scheduler}. *
*
Scheduler:
- *
You specify the {@link Scheduler} this operator runs on.
+ *
You specify the {@code Scheduler} this operator runs on.
*
* @param timeout the timeout value - * @param unit the timeout unit - * @param scheduler the scheduler to use to wait for completion - * @param other the other Completable instance to switch to in case of a timeout, - * if null a TimeoutException is emitted instead - * @return the new Completable instance - * @throws NullPointerException if unit or scheduler + * @param unit the unit of {@code timeout} + * @param scheduler the {@code Scheduler} to use to wait for completion + * @param fallback the other {@code Completable} instance to switch to in case of a timeout, + * if {@code null} a {@link TimeoutException} is emitted instead + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code fallback} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - private Completable timeout0(long timeout, TimeUnit unit, Scheduler scheduler, CompletableSource other) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new CompletableTimeout(this, timeout, unit, scheduler, other)); + private Completable timeout0(long timeout, TimeUnit unit, Scheduler scheduler, CompletableSource fallback) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new CompletableTimeout(this, timeout, unit, scheduler, fallback)); } /** - * Calls the specified converter function during assembly time and returns its resulting value. + * Calls the specified {@link CompletableConverter} function during assembly time and returns its resulting value. *

* *

@@ -2559,20 +3181,20 @@ private Completable timeout0(long timeout, TimeUnit unit, Scheduler scheduler, C * *

History: 2.1.7 - experimental * @param the resulting object type - * @param converter the function that receives the current Completable instance and returns a value + * @param converter the {@code CompletableConverter} that receives the current {@code Completable} instance and returns a value to be the result of {@code to()} * @return the converted value - * @throws NullPointerException if converter is null + * @throws NullPointerException if {@code converter} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public final R to(@NonNull CompletableConverter converter) { - return ObjectHelper.requireNonNull(converter, "converter is null").apply(this); + return Objects.requireNonNull(converter, "converter is null").apply(this); } /** - * Returns a Flowable which when subscribed to subscribes to this Completable and - * relays the terminal events to the subscriber. + * Returns a {@link Flowable} which when subscribed to subscribes to this {@code Completable} and + * relays the terminal events to the downstream {@link Subscriber}. *

* *

@@ -2582,21 +3204,44 @@ public final R to(@NonNull CompletableConverter converter) { *
{@code toFlowable} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @return the new Flowable instance + * @return the new {@code Flowable} instance */ @CheckReturnValue @SuppressWarnings("unchecked") @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable toFlowable() { + @NonNull + public final <@NonNull T> Flowable toFlowable() { if (this instanceof FuseToFlowable) { return ((FuseToFlowable)this).fuseToFlowable(); } - return RxJavaPlugins.onAssembly(new CompletableToFlowable(this)); + return RxJavaPlugins.onAssembly(new CompletableToFlowable<>(this)); + } + /** + * Returns a {@link Future} representing the termination of the current {@code Completable} + * via a {@code null} value. + *

+ * + *

+ * Cancelling the {@code Future} will cancel the subscription to the current {@code Completable}. + *

+ *
Scheduler:
+ *
{@code toFuture} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @return the new {@code Future} instance + * @see ReactiveX documentation: To + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Future toFuture() { + return subscribeWith(new FutureMultiObserver<>()); } /** - * Converts this Completable into a {@link Maybe}. + * Converts this {@code Completable} into a {@link Maybe}. *

* *

@@ -2605,22 +3250,22 @@ public final Flowable toFlowable() { *
* * @param the value type - * @return a {@link Maybe} that only calls {@code onComplete} or {@code onError}, based on which one is - * called by the source Completable. + * @return the new {@code Maybe} instance */ @CheckReturnValue @SuppressWarnings("unchecked") @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe toMaybe() { + @NonNull + public final <@NonNull T> Maybe toMaybe() { if (this instanceof FuseToMaybe) { return ((FuseToMaybe)this).fuseToMaybe(); } - return RxJavaPlugins.onAssembly(new MaybeFromCompletable(this)); + return RxJavaPlugins.onAssembly(new MaybeFromCompletable<>(this)); } /** - * Returns an Observable which when subscribed to subscribes to this Completable and - * relays the terminal events to the subscriber. + * Returns an {@link Observable} which when subscribed to subscribes to this {@code Completable} and + * relays the terminal events to the downstream {@link Observer}. *

* *

@@ -2628,21 +3273,22 @@ public final Maybe toMaybe() { *
{@code toObservable} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @return the new Observable created + * @return the new {@code Observable} created */ @CheckReturnValue @SuppressWarnings("unchecked") @SchedulerSupport(SchedulerSupport.NONE) - public final Observable toObservable() { + @NonNull + public final <@NonNull T> Observable toObservable() { if (this instanceof FuseToObservable) { return ((FuseToObservable)this).fuseToObservable(); } - return RxJavaPlugins.onAssembly(new CompletableToObservable(this)); + return RxJavaPlugins.onAssembly(new CompletableToObservable<>(this)); } /** - * Converts this Completable into a Single which when this Completable completes normally, - * calls the given supplier and emits its returned value through onSuccess. + * Converts this {@code Completable} into a {@link Single} which when this {@code Completable} completes normally, + * calls the given {@link Supplier} and emits its returned value through {@code onSuccess}. *

* *

@@ -2650,21 +3296,21 @@ public final Observable toObservable() { *
{@code toSingle} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param completionValueSupplier the value supplier called when this Completable completes normally - * @return the new Single instance - * @throws NullPointerException if completionValueSupplier is null + * @param completionValueSupplier the value supplier called when this {@code Completable} completes normally + * @return the new {@code Single} instance + * @throws NullPointerException if {@code completionValueSupplier} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single toSingle(final Supplier completionValueSupplier) { - ObjectHelper.requireNonNull(completionValueSupplier, "completionValueSupplier is null"); - return RxJavaPlugins.onAssembly(new CompletableToSingle(this, completionValueSupplier, null)); + public final <@NonNull T> Single toSingle(@NonNull Supplier completionValueSupplier) { + Objects.requireNonNull(completionValueSupplier, "completionValueSupplier is null"); + return RxJavaPlugins.onAssembly(new CompletableToSingle<>(this, completionValueSupplier, null)); } /** - * Converts this Completable into a Single which when this Completable completes normally, - * emits the given value through onSuccess. + * Converts this {@code Completable} into a {@link Single} which when this {@code Completable} completes normally, + * emits the given value through {@code onSuccess}. *

* *

@@ -2672,36 +3318,36 @@ public final Single toSingle(final Supplier completionValueS *
{@code toSingleDefault} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param completionValue the value to emit when this Completable completes normally - * @return the new Single instance - * @throws NullPointerException if completionValue is null + * @param completionValue the value to emit when this {@code Completable} completes normally + * @return the new {@code Single} instance + * @throws NullPointerException if {@code completionValue} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single toSingleDefault(final T completionValue) { - ObjectHelper.requireNonNull(completionValue, "completionValue is null"); - return RxJavaPlugins.onAssembly(new CompletableToSingle(this, null, completionValue)); + public final <@NonNull T> Single toSingleDefault(T completionValue) { + Objects.requireNonNull(completionValue, "completionValue is null"); + return RxJavaPlugins.onAssembly(new CompletableToSingle<>(this, null, completionValue)); } /** - * Returns a Completable which makes sure when a subscriber disposes the subscription, the - * dispose is called on the specified scheduler. + * Returns a {@code Completable} which makes sure when an observer disposes the subscription, the + * {@code dispose()} method is called on the specified {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code unsubscribeOn} calls dispose() of the upstream on the {@link Scheduler} you specify.
+ *
{@code unsubscribeOn} calls {@code dispose()} of the upstream on the {@code Scheduler} you specify.
*
- * @param scheduler the target scheduler where to execute the disposing - * @return the new Completable instance - * @throws NullPointerException if scheduler is null + * @param scheduler the target {@code Scheduler} where to execute the disposing + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Completable unsubscribeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Completable unsubscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new CompletableDisposeOn(this, scheduler)); } // ------------------------------------------------------------------------- @@ -2709,42 +3355,44 @@ public final Completable unsubscribeOn(final Scheduler scheduler) { // ------------------------------------------------------------------------- /** - * Creates a TestObserver and subscribes - * it to this Completable. + * Creates a {@link TestObserver} and subscribes + * it to this {@code Completable}. *

* *

*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new TestObserver instance + * @return the new {@code TestObserver} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); subscribe(to); return to; } /** - * Creates a TestObserver optionally in cancelled state, then subscribes it to this Completable. - * @param dispose if true, the TestObserver will be cancelled before subscribing to this - * Completable. + * Creates a {@link TestObserver} optionally in cancelled state, then subscribes it to this {@code Completable}. + * @param dispose if {@code true}, the {@code TestObserver} will be cancelled before subscribing to this + * {@code Completable}. *

* *

*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new TestObserver instance + * @return the new {@code TestObserver} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test(boolean dispose) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); if (dispose) { to.dispose(); @@ -2752,4 +3400,73 @@ public final TestObserver test(boolean dispose) { subscribe(to); return to; } + + // ------------------------------------------------------------------------- + // JDK 8 Support + // ------------------------------------------------------------------------- + + /** + * Signals completion (or error) when the {@link CompletionStage} terminates. + *

+ * + *

+ * Note that the operator takes an already instantiated, running or terminated {@code CompletionStage}. + * If the {@code CompletionStage} is to be created per consumer upon subscription, use {@link #defer(Supplier)} + * around {@code fromCompletionStage}: + *


+     * Maybe.defer(() -> Completable.fromCompletionStage(createCompletionStage()));
+     * 
+ *

+ * Canceling the flow can't cancel the execution of the {@code CompletionStage} because {@code CompletionStage} + * itself doesn't support cancellation. Instead, the operator detaches from the {@code CompletionStage}. + *

+ *
Scheduler:
+ *
{@code fromCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param stage the {@code CompletionStage} to convert to a {@code Completable} and + * signal {@code onComplete} or {@code onError} when the {@code CompletionStage} terminates normally or with a failure + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code stage} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static Completable fromCompletionStage(@NonNull CompletionStage stage) { + Objects.requireNonNull(stage, "stage is null"); + return RxJavaPlugins.onAssembly(new CompletableFromCompletionStage<>(stage)); + } + + /** + * Signals the given default item when the upstream completes or signals the upstream error via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).toCompletionStage(Optional.empty());
+     * 
+ *
+ *
Scheduler:
+ *
{@code toCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the type of the default item to signal upon completion + * @param defaultItem the item to signal upon completion + * @return the new {@code CompletionStage} instance + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@Nullable T> CompletionStage toCompletionStage(T defaultItem) { + return subscribeWith(new CompletionStageConsumer<>(true, defaultItem)); + } } diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableConverter.java b/src/main/java/io/reactivex/rxjava3/core/CompletableConverter.java index 6ef04e2608..a213c6f5ab 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableConverter.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableConverter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,13 +22,13 @@ * @param the output type * @since 2.2 */ -public interface CompletableConverter { +@FunctionalInterface +public interface CompletableConverter<@NonNull R> { /** * Applies a function to the upstream Completable and returns a converted value of type {@code R}. * * @param upstream the upstream Completable instance * @return the converted value */ - @NonNull R apply(@NonNull Completable upstream); } diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableEmitter.java b/src/main/java/io/reactivex/rxjava3/core/CompletableEmitter.java index 51c9780f73..2c5a9811c4 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableEmitter.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableEmitter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -82,12 +82,12 @@ public interface CompletableEmitter { boolean isDisposed(); /** - * Attempts to emit the specified {@code Throwable} error if the downstream + * Attempts to emit the specified {@link Throwable} error if the downstream * hasn't cancelled the sequence or is otherwise terminated, returning false * if the emission is not allowed to happen due to lifecycle restrictions. *

- * Unlike {@link #onError(Throwable)}, the {@code RxJavaPlugins.onError} is not called - * if the error could not be delivered. + * Unlike {@link #onError(Throwable)}, the {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable) RxjavaPlugins.onError} + * is not called if the error could not be delivered. *

History: 2.1.1 - experimental * @param t the throwable error to signal if possible * @return true if successful, false if the downstream is not able to accept further diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableObserver.java b/src/main/java/io/reactivex/rxjava3/core/CompletableObserver.java index af0a017d66..9339307047 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -49,9 +49,9 @@ */ public interface CompletableObserver { /** - * Called once by the Completable to set a Disposable on this instance which + * Called once by the {@link Completable} to set a {@link Disposable} on this instance which * then can be used to cancel the subscription at any time. - * @param d the Disposable instance to call dispose on for cancellation, not null + * @param d the {@code Disposable} instance to call dispose on for cancellation, not null */ void onSubscribe(@NonNull Disposable d); @@ -62,7 +62,7 @@ public interface CompletableObserver { /** * Called once if the deferred computation 'throws' an exception. - * @param e the exception, not null. + * @param e the exception, not {@code null}. */ void onError(@NonNull Throwable e); } diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java index 8dc2531997..e73fae2d5c 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableOnSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,20 +10,22 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link CompletableEmitter} instance that allows pushing + * a {@link CompletableEmitter} instance that allows pushing * an event in a cancellation-safe manner. */ +@FunctionalInterface public interface CompletableOnSubscribe { /** - * Called for each CompletableObserver that subscribes. - * @param emitter the safe emitter instance, never null + * Called for each {@link CompletableObserver} that subscribes. + * @param emitter the safe emitter instance, never {@code null} * @throws Throwable on error */ void subscribe(@NonNull CompletableEmitter emitter) throws Throwable; diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableOperator.java b/src/main/java/io/reactivex/rxjava3/core/CompletableOperator.java index 3a45c9085f..f06a94f36a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableOperator.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableOperator.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,13 +18,14 @@ /** * Interface to map/wrap a downstream observer to an upstream observer. */ +@FunctionalInterface public interface CompletableOperator { /** - * Applies a function to the child CompletableObserver and returns a new parent CompletableObserver. - * @param observer the child CompletableObservable instance - * @return the parent CompletableObserver instance - * @throws Exception on failure + * Applies a function to the child {@link CompletableObserver} and returns a new parent {@code CompletableObserver}. + * @param observer the child {@code CompletableObserver} instance + * @return the parent {@code CompletableObserver} instance + * @throws Throwable on failure */ @NonNull - CompletableObserver apply(@NonNull CompletableObserver observer) throws Exception; + CompletableObserver apply(@NonNull CompletableObserver observer) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableSource.java b/src/main/java/io/reactivex/rxjava3/core/CompletableSource.java index 390be60b2e..90d3853b8a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableSource.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; @@ -20,12 +21,13 @@ * * @since 2.0 */ +@FunctionalInterface public interface CompletableSource { /** - * Subscribes the given CompletableObserver to this CompletableSource instance. - * @param co the CompletableObserver, not null - * @throws NullPointerException if {@code co} is null + * Subscribes the given {@link CompletableObserver} to this {@code CompletableSource} instance. + * @param observer the {@code CompletableObserver}, not {@code null} + * @throws NullPointerException if {@code observer} is {@code null} */ - void subscribe(@NonNull CompletableObserver co); + void subscribe(@NonNull CompletableObserver observer); } diff --git a/src/main/java/io/reactivex/rxjava3/core/CompletableTransformer.java b/src/main/java/io/reactivex/rxjava3/core/CompletableTransformer.java index b6acc252b0..2887c4717c 100644 --- a/src/main/java/io/reactivex/rxjava3/core/CompletableTransformer.java +++ b/src/main/java/io/reactivex/rxjava3/core/CompletableTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,14 +16,15 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Convenience interface and callback used by the compose operator to turn a Completable into another - * Completable fluently. + * Convenience interface and callback used by the compose operator to turn a {@link Completable} into another + * {@code Completable} fluently. */ +@FunctionalInterface public interface CompletableTransformer { /** - * Applies a function to the upstream Completable and returns a CompletableSource. - * @param upstream the upstream Completable instance - * @return the transformed CompletableSource instance + * Applies a function to the upstream {@link Completable} and returns a {@link CompletableSource}. + * @param upstream the upstream {@code Completable} instance + * @return the transformed {@code CompletableSource} instance */ @NonNull CompletableSource apply(@NonNull Completable upstream); diff --git a/src/main/java/io/reactivex/rxjava3/core/Emitter.java b/src/main/java/io/reactivex/rxjava3/core/Emitter.java index e222f9c44f..83410f056e 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Emitter.java +++ b/src/main/java/io/reactivex/rxjava3/core/Emitter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; @@ -25,17 +26,17 @@ * * @param the value type emitted */ -public interface Emitter { +public interface Emitter<@NonNull T> { /** * Signal a normal value. - * @param value the value to signal, not null + * @param value the value to signal, not {@code null} */ void onNext(@NonNull T value); /** - * Signal a Throwable exception. - * @param error the Throwable to signal, not null + * Signal a {@link Throwable} exception. + * @param error the {@code Throwable} to signal, not {@code null} */ void onError(@NonNull Throwable error); diff --git a/src/main/java/io/reactivex/rxjava3/core/Flowable.java b/src/main/java/io/reactivex/rxjava3/core/Flowable.java index 5cb5ebd7ed..39f3c63b43 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Flowable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Flowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,46 +10,51 @@ * 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 io.reactivex.rxjava3.core; import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.flowables.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.internal.jdk8.*; import io.reactivex.rxjava3.internal.operators.flowable.*; +import io.reactivex.rxjava3.internal.operators.maybe.MaybeToFlowable; import io.reactivex.rxjava3.internal.operators.mixed.*; import io.reactivex.rxjava3.internal.operators.observable.ObservableFromPublisher; +import io.reactivex.rxjava3.internal.operators.single.SingleToFlowable; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscribers.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.ScalarSupplier; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; /** - * The Flowable class that implements the Reactive Streams + * The {@code Flowable} class that implements the Reactive Streams {@link Publisher} * Pattern and offers factory methods, intermediate operators and the ability to consume reactive dataflows. *

- * Reactive Streams operates with {@link Publisher}s which {@code Flowable} extends. Many operators + * Reactive Streams operates with {@code Publisher}s which {@code Flowable} extends. Many operators * therefore accept general {@code Publisher}s directly and allow direct interoperation with other - * Reactive Streams implementations. + * Reactive Streams implementations. *

- * The Flowable hosts the default buffer size of 128 elements for operators, accessible via {@link #bufferSize()}, + * The {@code Flowable} hosts the default buffer size of 128 elements for operators, accessible via {@link #bufferSize()}, * that can be overridden globally via the system parameter {@code rx3.buffer-size}. Most operators, however, have * overloads that allow setting their internal buffer size explicitly. *

* The documentation for this class makes use of marble diagrams. The following legend explains these diagrams: *

- * + * *

* The {@code Flowable} follows the protocol *


@@ -60,7 +65,7 @@
  * Unlike the {@code Observable.subscribe()} of version 1.x, {@link #subscribe(Subscriber)} does not allow external cancellation
  * of a subscription and the {@link Subscriber} instance is expected to expose such capability if needed.
  * 

- * Flowables support backpressure and require {@link Subscriber}s to signal demand via {@link Subscription#request(long)}. + * {@code Flowable}s support backpressure and require {@code Subscriber}s to signal demand via {@link Subscription#request(long)}. *

* Example: *


@@ -88,13 +93,13 @@
  * d.dispose();
  * 
*

- * The Reactive Streams specification is relatively strict when defining interactions between {@code Publisher}s and {@code Subscriber}s, so much so + * The Reactive Streams specification is relatively strict when defining interactions between {@code Publisher}s and {@code Subscriber}s, so much so * that there is a significant performance penalty due certain timing requirements and the need to prepare for invalid * request amounts via {@link Subscription#request(long)}. * Therefore, RxJava has introduced the {@link FlowableSubscriber} interface that indicates the consumer can be driven with relaxed rules. * All RxJava operators are implemented with these relaxed rules in mind. - * If the subscribing {@code Subscriber} does not implement this interface, for example, due to it being from another Reactive Streams compliant - * library, the Flowable will automatically apply a compliance wrapper around it. + * If the subscribing {@code Subscriber} does not implement this interface, for example, due to it being from another Reactive Streams compliant + * library, the {@code Flowable} will automatically apply a compliance wrapper around it. *

* {@code Flowable} is an abstract class, but it is not advised to implement sources and custom operators by extending the class directly due * to the large amounts of Reactive Streams @@ -140,16 +145,15 @@ * has to be explicitly expressed via operators such as {@link #subscribeOn(Scheduler)}, {@link #observeOn(Scheduler)} and {@link #parallel()}. In general, * operators featuring a {@link Scheduler} parameter are introducing this type of asynchrony into the flow. *

- * For more information see the ReactiveX - * documentation. + * For more information see the ReactiveX documentation. * * @param - * the type of the items emitted by the Flowable + * the type of the items emitted by the {@code Flowable} * @see Observable * @see ParallelFlowable * @see io.reactivex.rxjava3.subscribers.DisposableSubscriber */ -public abstract class Flowable implements Publisher { +public abstract class Flowable<@NonNull T> implements Publisher { /** The default buffer size. */ static final int BUFFER_SIZE; static { @@ -157,62 +161,79 @@ public abstract class Flowable implements Publisher { } /** - * Mirrors the one Publisher in an Iterable of several Publishers that first either emits an item or sends + * Mirrors the one {@link Publisher} in an {@link Iterable} of several {@code Publisher}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code Publisher}s signal an item or terminates first, all subscriptions to the other + * {@code Publisher}s are canceled. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning * {@code Publisher}'s backpressure behavior.
*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code Publisher}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type * @param sources - * an Iterable of Publishers sources competing to react first. A subscription to each Publisher will - * occur in the same order as in this Iterable. - * @return a Flowable that emits the same sequence as whichever of the source Publishers first - * emitted an item or sent a termination notification + * an {@code Iterable} of {@code Publisher}s sources competing to react first. A subscription to each {@code Publisher} will + * occur in the same order as in this {@code Iterable}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Amb */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable amb(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new FlowableAmb(null, sources)); + public static <@NonNull T> Flowable amb(@NonNull Iterable<@NonNull ? extends Publisher> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableAmb<>(null, sources)); } /** - * Mirrors the one Publisher in an array of several Publishers that first either emits an item or sends + * Mirrors the one {@link Publisher} in an array of several {@code Publisher}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code Publisher}s signal an item or terminates first, all subscriptions to the other + * {@code Publisher}s are canceled. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning * {@code Publisher}'s backpressure behavior.
*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code Publisher}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type * @param sources - * an array of Publisher sources competing to react first. A subscription to each Publisher will - * occur in the same order as in this Iterable. - * @return a Flowable that emits the same sequence as whichever of the source Publishers first - * emitted an item or sent a termination notification + * an array of {@code Publisher} sources competing to react first. A subscription to each {@code Publisher} will + * occur in the same order as in this array. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Amb */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable ambArray(Publisher... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T> Flowable ambArray(@NonNull Publisher... sources) { + Objects.requireNonNull(sources, "sources is null"); int len = sources.length; if (len == 0) { return empty(); @@ -220,40 +241,41 @@ public static Flowable ambArray(Publisher... sources) { if (len == 1) { return fromPublisher(sources[0]); } - return RxJavaPlugins.onAssembly(new FlowableAmb(sources, null)); + return RxJavaPlugins.onAssembly(new FlowableAmb<>(sources, null)); } /** * Returns the default internal buffer size used by most async operators. *

The value can be overridden via system parameter {@code rx3.buffer-size} - * before the Flowable class is loaded. + * before the {@code Flowable} class is loaded. * @return the default internal buffer size. */ + @CheckReturnValue public static int bufferSize() { return BUFFER_SIZE; } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided array of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatestArray} does not operate by default on a particular {@link Scheduler}.
*
@@ -263,41 +285,42 @@ public static int bufferSize() { * @param * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatestArray(Publisher[] sources, Function combiner) { + @NonNull + public static <@NonNull T, @NonNull R> Flowable combineLatestArray(@NonNull Publisher[] sources, @NonNull Function combiner) { return combineLatestArray(sources, combiner, bufferSize()); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided array of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatestArray} does not operate by default on a particular {@link Scheduler}.
*
@@ -307,50 +330,51 @@ public static Flowable combineLatestArray(Publisher[] sou * @param * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers + * the aggregation function used to combine the items emitted by the source {@code Publisher}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Flowable - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the internal buffer size and prefetch amount applied to every source {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatestArray(Publisher[] sources, Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T, @NonNull R> Flowable combineLatestArray(@NonNull Publisher[] sources, @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } - ObjectHelper.requireNonNull(combiner, "combiner is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableCombineLatest(sources, combiner, bufferSize, false)); + return RxJavaPlugins.onAssembly(new FlowableCombineLatest<>(sources, combiner, bufferSize, false)); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided iterable of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -360,42 +384,43 @@ public static Flowable combineLatestArray(Publisher[] sou * @param * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatest(Iterable> sources, - Function combiner) { + @NonNull + public static <@NonNull T, @NonNull R> Flowable combineLatest(@NonNull Iterable<@NonNull ? extends Publisher> sources, + @NonNull Function combiner) { return combineLatest(sources, combiner, bufferSize()); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of source Publishers is empty, the resulting sequence completes immediately without emitting any items and + * If the provided iterable of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting any items and * without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -405,50 +430,51 @@ public static Flowable combineLatest(Iterable * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers + * the aggregation function used to combine the items emitted by the source {@code Publisher}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Flowable - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the internal buffer size and prefetch amount applied to every source {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatest(Iterable> sources, - Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + public static <@NonNull T, @NonNull R> Flowable combineLatest(@NonNull Iterable<@NonNull ? extends Publisher> sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableCombineLatest(sources, combiner, bufferSize, false)); + return RxJavaPlugins.onAssembly(new FlowableCombineLatest<>(sources, combiner, bufferSize, false)); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided array of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
- *
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
{@code combineLatestArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param @@ -456,45 +482,46 @@ public static Flowable combineLatest(Iterable * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatestDelayError(Publisher[] sources, - Function combiner) { - return combineLatestDelayError(sources, combiner, bufferSize()); + @NonNull + public static <@NonNull T, @NonNull R> Flowable combineLatestArrayDelayError(@NonNull Publisher[] sources, + @NonNull Function combiner) { + return combineLatestArrayDelayError(sources, combiner, bufferSize()); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function and delays any error from the sources until - * all source Publishers terminate. + * all source {@code Publisher}s terminate. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided array of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
- *
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
{@code combineLatestArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param @@ -502,52 +529,53 @@ public static Flowable combineLatestDelayError(Publisher[ * @param * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers + * the aggregation function used to combine the items emitted by the source {@code Publisher}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Flowable - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the internal buffer size and prefetch amount applied to every source {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatestDelayError(Publisher[] sources, - Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + public static <@NonNull T, @NonNull R> Flowable combineLatestArrayDelayError(@NonNull Publisher[] sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (sources.length == 0) { return empty(); } - return RxJavaPlugins.onAssembly(new FlowableCombineLatest(sources, combiner, bufferSize, true)); + return RxJavaPlugins.onAssembly(new FlowableCombineLatest<>(sources, combiner, bufferSize, true)); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function and delays any error from the sources until - * all source Publishers terminate. + * all source {@code Publisher}s terminate. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided iterable of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -557,43 +585,44 @@ public static Flowable combineLatestDelayError(Publisher[ * @param * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatestDelayError(Iterable> sources, - Function combiner) { + @NonNull + public static <@NonNull T, @NonNull R> Flowable combineLatestDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources, + @NonNull Function combiner) { return combineLatestDelayError(sources, combiner, bufferSize()); } /** - * Combines a collection of source Publishers by emitting an item that aggregates the latest values of each of - * the source Publishers each time an item is received from any of the source Publishers, where this + * Combines a collection of source {@link Publisher}s by emitting an item that aggregates the latest values of each of + * the source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function and delays any error from the sources until - * all source Publishers terminate. + * all source {@code Publisher}s terminate. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of source Publishers is empty, the resulting sequence completes immediately without emitting + * If the provided iterable of source {@code Publisher}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -603,41 +632,43 @@ public static Flowable combineLatestDelayError(Iterable * the result type * @param sources - * the collection of source Publishers + * the collection of source {@code Publisher}s * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers + * the aggregation function used to combine the items emitted by the source {@code Publisher}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Flowable - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the internal buffer size and prefetch amount applied to every source {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) - public static Flowable combineLatestDelayError(Iterable> sources, - Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + @NonNull + public static <@NonNull T, @NonNull R> Flowable combineLatestDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableCombineLatest(sources, combiner, bufferSize, true)); + return RxJavaPlugins.onAssembly(new FlowableCombineLatest<>(sources, combiner, bufferSize, true)); } /** - * Combines two source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from either of the source Publishers, where this + * Combines two source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from either of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -646,42 +677,44 @@ public static Flowable combineLatestDelayError(Iterable the element type of the second source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - BiFunction combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull BiFunction combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines three source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines three source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -691,15 +724,15 @@ public static Flowable combineLatest( * @param the element type of the third source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -707,31 +740,32 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, - Function3 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, + @NonNull Function3 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines four source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines four source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -742,17 +776,18 @@ public static Flowable combineLatest( * @param the element type of the fourth source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param source4 - * the fourth source Publisher + * the fourth source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -760,32 +795,33 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Function4 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Function4 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3, source4 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines five source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines five source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -797,19 +833,20 @@ public static Flowable combineLatest( * @param the element type of the fifth source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param source4 - * the fourth source Publisher + * the fourth source {@code Publisher} * @param source5 - * the fifth source Publisher + * the fifth source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -817,34 +854,35 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Publisher source5, - Function5 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Publisher source5, + @NonNull Function5 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3, source4, source5 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines six source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines six source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -857,21 +895,22 @@ public static Flowable combineLatest( * @param the element type of the sixth source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param source4 - * the fourth source Publisher + * the fourth source {@code Publisher} * @param source5 - * the fifth source Publisher + * the fifth source {@code Publisher} * @param source6 - * the sixth source Publisher + * the sixth source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -879,35 +918,36 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Publisher source5, Publisher source6, - Function6 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Function6 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3, source4, source5, source6 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines seven source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines seven source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -921,23 +961,25 @@ public static Flowable combineLatest( * @param the element type of the seventh source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param source4 - * the fourth source Publisher + * the fourth source {@code Publisher} * @param source5 - * the fifth source Publisher + * the fifth source {@code Publisher} * @param source6 - * the sixth source Publisher + * the sixth source {@code Publisher} * @param source7 - * the seventh source Publisher + * the seventh source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -945,37 +987,38 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Publisher source5, Publisher source6, - Publisher source7, - Function7 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Publisher source7, + @NonNull Function7 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3, source4, source5, source6, source7 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines eight source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines eight source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -990,25 +1033,27 @@ public static Flowable combineLatest( * @param the element type of the eighth source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param source4 - * the fourth source Publisher + * the fourth source {@code Publisher} * @param source5 - * the fifth source Publisher + * the fifth source {@code Publisher} * @param source6 - * the sixth source Publisher + * the sixth source {@code Publisher} * @param source7 - * the seventh source Publisher + * the seventh source {@code Publisher} * @param source8 - * the eighth source Publisher + * the eighth source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -1016,38 +1061,39 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Publisher source5, Publisher source6, - Publisher source7, Publisher source8, - Function8 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Publisher source7, @NonNull Publisher source8, + @NonNull Function8 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3, source4, source5, source6, source7, source8 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines nine source Publishers by emitting an item that aggregates the latest values of each of the - * source Publishers each time an item is received from any of the source Publishers, where this + * Combines nine source {@link Publisher}s by emitting an item that aggregates the latest values of each of the + * source {@code Publisher}s each time an item is received from any of the source {@code Publisher}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated until that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Backpressure:
*
The returned {@code Publisher} honors backpressure from downstream. The source {@code Publisher}s * are requested in a bounded manner, however, their backpressure is not enforced (the operator won't signal - * {@code MissingBackpressureException}) and may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * {@link MissingBackpressureException}) and may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
*
@@ -1063,27 +1109,30 @@ public static Flowable combineLatest( * @param the element type of the ninth source * @param the combined output type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * the second source Publisher + * the second source {@code Publisher} * @param source3 - * the third source Publisher + * the third source {@code Publisher} * @param source4 - * the fourth source Publisher + * the fourth source {@code Publisher} * @param source5 - * the fifth source Publisher + * the fifth source {@code Publisher} * @param source6 - * the sixth source Publisher + * the sixth source {@code Publisher} * @param source7 - * the seventh source Publisher + * the seventh source {@code Publisher} * @param source8 - * the eighth source Publisher + * the eighth source {@code Publisher} * @param source9 - * the ninth source Publisher + * the ninth source {@code Publisher} * @param combiner - * the aggregation function used to combine the items emitted by the source Publishers - * @return a Flowable that emits items that are the result of combining the items emitted by the source - * Publishers by means of the given aggregation function + * the aggregation function used to combine the items emitted by the source {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8}, {@code source9} + * or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @@ -1091,350 +1140,363 @@ public static Flowable combineLatest( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable combineLatest( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Publisher source5, Publisher source6, - Publisher source7, Publisher source8, - Publisher source9, - Function9 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); - ObjectHelper.requireNonNull(source9, "source9 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> Flowable combineLatest( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Publisher source7, @NonNull Publisher source8, + @NonNull Publisher source9, + @NonNull Function9 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new Publisher[] { source1, source2, source3, source4, source5, source6, source7, source8, source9 }, Functions.toFunction(combiner), bufferSize()); } /** - * Concatenates elements of each Publisher provided via an Iterable sequence into a single sequence + * Concatenates elements of each {@link Publisher} provided via an {@link Iterable} sequence into a single sequence * of elements without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when that {@code Publisher} completes. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the common value type of the sources - * @param sources the Iterable sequence of Publishers - * @return the new Flowable instance + * @param sources the {@code Iterable} sequence of {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Flowable concat(@NonNull Iterable<@NonNull ? extends Publisher> sources) { + Objects.requireNonNull(sources, "sources is null"); // unlike general sources, fromIterable can only throw on a boundary because it is consumed only there return fromIterable(sources).concatMapDelayError((Function)Functions.identity(), false, 2); } /** - * Returns a Flowable that emits the items emitted by each of the Publishers emitted by the source - * Publisher, one after the other, without interleaving them. + * Returns a {@code Flowable} that emits the items emitted by each of the {@link Publisher}s emitted by the source + * {@code Publisher}, one after the other, without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher} * sources are expected to honor backpressure as well. If the outer violates this, a - * {@code MissingBackpressureException} is signaled. If any of the inner {@code Publisher}s violates - * this, it may throw an {@code IllegalStateException} when an inner {@code Publisher} completes.
+ * {@link MissingBackpressureException} is signaled. If any of the inner {@code Publisher}s violates + * this, it may throw an {@link IllegalStateException} when an inner {@code Publisher} completes. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * a Publisher that emits Publishers - * @return a Flowable that emits items all of the items emitted by the Publishers emitted by - * {@code Publishers}, one after the other, without interleaving them + * a {@code Publisher} that emits {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Concat */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable concat(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return concat(sources, bufferSize()); } /** - * Returns a Flowable that emits the items emitted by each of the Publishers emitted by the source - * Publisher, one after the other, without interleaving them. + * Returns a {@code Flowable} that emits the items emitted by each of the {@link Publisher}s emitted by the source + * {@code Publisher}, one after the other, without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher} * sources are expected to honor backpressure as well. If the outer violates this, a - * {@code MissingBackpressureException} is signaled. If any of the inner {@code Publisher}s violates - * this, it may throw an {@code IllegalStateException} when an inner {@code Publisher} completes.
+ * {@link MissingBackpressureException} is signaled. If any of the inner {@code Publisher}s violates + * this, it may throw an {@link IllegalStateException} when an inner {@code Publisher} completes. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * a Publisher that emits Publishers + * a {@code Publisher} that emits {@code Publisher}s * @param prefetch - * the number of Publishers to prefetch from the sources sequence. - * @return a Flowable that emits items all of the items emitted by the Publishers emitted by - * {@code Publishers}, one after the other, without interleaving them + * the number of {@code Publisher}s to prefetch from the sources sequence. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: Concat */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Publisher> sources, int prefetch) { + @NonNull + public static <@NonNull T> Flowable concat(@NonNull Publisher<@NonNull ? extends Publisher> sources, int prefetch) { return fromPublisher(sources).concatMap((Function)Functions.identity(), prefetch); } /** - * Returns a Flowable that emits the items emitted by two Publishers, one after the other, without + * Returns a {@code Flowable} that emits the items emitted by two {@link Publisher}s, one after the other, without * interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when that source {@code Publisher} completes. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param source1 - * a Publisher to be concatenated + * a {@code Publisher} to be concatenated * @param source2 - * a Publisher to be concatenated - * @return a Flowable that emits items emitted by the two source Publishers, one after the other, - * without interleaving them + * a {@code Publisher} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Concat */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Publisher source1, Publisher source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T> Flowable concat(@NonNull Publisher source1, @NonNull Publisher source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return concatArray(source1, source2); } /** - * Returns a Flowable that emits the items emitted by three Publishers, one after the other, without + * Returns a {@code Flowable} that emits the items emitted by three {@link Publisher}s, one after the other, without * interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when that source {@code Publisher} completes. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param source1 - * a Publisher to be concatenated + * a {@code Publisher} to be concatenated * @param source2 - * a Publisher to be concatenated + * a {@code Publisher} to be concatenated * @param source3 - * a Publisher to be concatenated - * @return a Flowable that emits items emitted by the three source Publishers, one after the other, - * without interleaving them + * a {@code Publisher} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Concat */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat( - Publisher source1, Publisher source2, - Publisher source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T> Flowable concat( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return concatArray(source1, source2, source3); } /** - * Returns a Flowable that emits the items emitted by four Publishers, one after the other, without + * Returns a {@code Flowable} that emits the items emitted by four {@link Publisher}s, one after the other, without * interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when that source {@code Publisher} completes. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param source1 - * a Publisher to be concatenated + * a {@code Publisher} to be concatenated * @param source2 - * a Publisher to be concatenated + * a {@code Publisher} to be concatenated * @param source3 - * a Publisher to be concatenated + * a {@code Publisher} to be concatenated * @param source4 - * a Publisher to be concatenated - * @return a Flowable that emits items emitted by the four source Publishers, one after the other, - * without interleaving them + * a {@code Publisher} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Concat */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T> Flowable concat( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return concatArray(source1, source2, source3, source4); } /** - * Concatenates a variable number of Publisher sources. + * Concatenates a variable number of {@link Publisher} sources. *

- * Note: named this way because of overload conflict with concat(Publisher<Publisher>). + * Note: named this way because of overload conflict with {@code concat(Publisher>}). *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when that source {@code Publisher} completes. *
Scheduler:
*
{@code concatArray} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the array of sources + * @param sources the array of source {@code Publisher}s * @param the common base value type - * @return the new Publisher instance - * @throws NullPointerException if sources is null + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatArray(Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable concatArray(@NonNull Publisher... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } else if (sources.length == 1) { return fromPublisher(sources[0]); } - return RxJavaPlugins.onAssembly(new FlowableConcatArray(sources, false)); + return RxJavaPlugins.onAssembly(new FlowableConcatArray<>(sources, false)); } /** - * Concatenates a variable number of Publisher sources and delays errors from any of them + * Concatenates a variable number of {@link Publisher} sources and delays errors from any of them * till all terminate. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when that source {@code Publisher} completes. *
Scheduler:
*
{@code concatArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
- * @param sources the array of sources + * @param sources the array of source {@code Publisher}s * @param the common base value type - * @return the new Flowable instance - * @throws NullPointerException if sources is null + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatArrayDelayError(Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable concatArrayDelayError(@NonNull Publisher... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } else if (sources.length == 1) { return fromPublisher(sources[0]); } - return RxJavaPlugins.onAssembly(new FlowableConcatArray(sources, true)); + return RxJavaPlugins.onAssembly(new FlowableConcatArray<>(sources, true)); } /** - * Concatenates an array of Publishers eagerly into a single stream of values. + * Concatenates an array of {@link Publisher}s eagerly into a single stream of values. *

* *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them + * source {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them * in order, each one after the previous one completes. *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, the operator will signal a - * {@code MissingBackpressureException}.
+ * {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources an array of Publishers that need to be eagerly concatenated - * @return the new Publisher instance with the specified concatenation behavior + * @param sources an array of {@code Publisher}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatArrayEager(Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable concatArrayEager(@NonNull Publisher... sources) { return concatArrayEager(bufferSize(), bufferSize(), sources); } /** - * Concatenates an array of Publishers eagerly into a single stream of values. + * Concatenates an array of {@link Publisher}s eagerly into a single stream of values. *

* *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them + * source {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them * in order, each one after the previous one completes. *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, the operator will signal a - * {@code MissingBackpressureException}.
+ * {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources an array of Publishers that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrent subscriptions at a time, Integer.MAX_VALUE + * @param sources an array of {@code Publisher}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrent subscriptions at a time, {@link Integer#MAX_VALUE} * is interpreted as an indication to subscribe to all sources at once - * @param prefetch the number of elements to prefetch from each Publisher source - * @return the new Publisher instance with the specified concatenation behavior + * @param prefetch the number of elements to prefetch from each {@code Publisher} source + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive * @since 2.0 */ @CheckReturnValue @@ -1442,8 +1504,9 @@ public static Flowable concatArrayEager(Publisher... sources @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings({ "rawtypes", "unchecked" }) - public static Flowable concatArrayEager(int maxConcurrency, int prefetch, Publisher... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T> Flowable concatArrayEager(int maxConcurrency, int prefetch, @NonNull Publisher... sources) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(prefetch, "prefetch"); return RxJavaPlugins.onAssembly(new FlowableConcatMapEager(new FlowableFromArray(sources), Functions.identity(), maxConcurrency, prefetch, ErrorMode.IMMEDIATE)); @@ -1463,19 +1526,22 @@ public static Flowable concatArrayEager(int maxConcurrency, int prefetch, *
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, the operator will signal a - * {@code MissingBackpressureException}.
+ * {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
* * @param the value type * @param sources an array of {@code Publisher}s that need to be eagerly concatenated - * @return the new Flowable instance with the specified concatenation behavior + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.2.1 - experimental */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Flowable concatArrayEagerDelayError(Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable concatArrayEagerDelayError(@NonNull Publisher... sources) { return concatArrayEagerDelayError(bufferSize(), bufferSize(), sources); } @@ -1493,57 +1559,62 @@ public static Flowable concatArrayEagerDelayError(Publisher. *
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. * If any of the source {@code Publisher}s violate this, the operator will signal a - * {@code MissingBackpressureException}.
+ * {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
* * @param the value type * @param sources an array of {@code Publisher}s that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrent subscriptions at a time, Integer.MAX_VALUE + * @param maxConcurrency the maximum number of concurrent subscriptions at a time, {@link Integer#MAX_VALUE} * is interpreted as indication to subscribe to all sources at once * @param prefetch the number of elements to prefetch from each {@code Publisher} source - * @return the new Flowable instance with the specified concatenation behavior + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive * @since 2.2.1 - experimental */ @SuppressWarnings({ "rawtypes", "unchecked" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Flowable concatArrayEagerDelayError(int maxConcurrency, int prefetch, Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable concatArrayEagerDelayError(int maxConcurrency, int prefetch, @NonNull Publisher... sources) { return fromArray(sources).concatMapEagerDelayError((Function)Functions.identity(), true, maxConcurrency, prefetch); } /** - * Concatenates the Iterable sequence of Publishers into a single sequence by subscribing to each Publisher, - * one after the other, one at a time and delays any errors till the all inner Publishers terminate. + * Concatenates the {@link Iterable} sequence of {@link Publisher}s into a single sequence by subscribing to each {@code Publisher}, + * one after the other, one at a time and delays any errors till the all inner {@code Publisher}s terminate. * *
*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher} * sources are expected to honor backpressure as well. If the outer violates this, a - * {@code MissingBackpressureException} is signaled. If any of the inner {@code Publisher}s violates - * this, it may throw an {@code IllegalStateException} when an inner {@code Publisher} completes.
+ * {@link MissingBackpressureException} is signaled. If any of the inner {@code Publisher}s violates + * this, it may throw an {@link IllegalStateException} when an inner {@code Publisher} completes. *
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type - * @param sources the Iterable sequence of Publishers - * @return the new Publisher with the concatenating behavior + * @param sources the {@code Iterable} sequence of {@code Publisher}s + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatDelayError(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Flowable concatDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources) { + Objects.requireNonNull(sources, "sources is null"); return fromIterable(sources).concatMapDelayError((Function)Functions.identity()); } /** - * Concatenates the Publisher sequence of Publishers into a single sequence by subscribing to each inner Publisher, - * one after the other, one at a time and delays any errors till the all inner and the outer Publishers terminate. + * Concatenates the {@link Publisher} sequence of {@code Publisher}s into a single sequence by subscribing to each inner {@code Publisher}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code Publisher}s terminate. * *
*
Backpressure:
@@ -1553,19 +1624,21 @@ public static Flowable concatDelayError(Iterable * * @param the common element base type - * @param sources the Publisher sequence of Publishers - * @return the new Publisher with the concatenating behavior + * @param sources the {@code Publisher} sequence of {@code Publisher}s + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatDelayError(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable concatDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return concatDelayError(sources, bufferSize(), true); } /** - * Concatenates the Publisher sequence of Publishers into a single sequence by subscribing to each inner Publisher, - * one after the other, one at a time and delays any errors till the all inner and the outer Publishers terminate. + * Concatenates the {@link Publisher} sequence of {@code Publisher}s into a single sequence by subscribing to each inner {@code Publisher}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code Publisher}s terminate. * *
*
Backpressure:
@@ -1575,66 +1648,78 @@ public static Flowable concatDelayError(Publisher * * @param the common element base type - * @param sources the Publisher sequence of Publishers - * @param prefetch the number of elements to prefetch from the outer Publisher - * @param tillTheEnd if true exceptions from the outer and all inner Publishers are delayed to the end - * if false, exception from the outer Publisher is delayed till the current Publisher terminates - * @return the new Publisher with the concatenating behavior + * @param sources the {@code Publisher} sequence of {@code Publisher}s + * @param prefetch the number of elements to prefetch from the outer {@code Publisher} + * @param tillTheEnd if {@code true}, exceptions from the outer and all inner {@code Publisher}s are delayed to the end + * if {@code false}, exception from the outer {@code Publisher} is delayed till the current inner {@code Publisher} terminates + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is {@code null} */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatDelayError(Publisher> sources, int prefetch, boolean tillTheEnd) { + @NonNull + public static <@NonNull T> Flowable concatDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources, int prefetch, boolean tillTheEnd) { return fromPublisher(sources).concatMapDelayError((Function)Functions.identity(), tillTheEnd, prefetch); } /** - * Concatenates a Publisher sequence of Publishers eagerly into a single stream of values. + * Concatenates a sequence of {@link Publisher}s eagerly into a single stream of values. + *

+ * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * emitted source Publishers as they are observed. The operator buffers the values emitted by these - * Publishers and then drains them in order, each one after the previous one completes. + * source {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them + * in order, each one after the previous one completes. *

*
Backpressure:
- *
Backpressure is honored towards the downstream and both the outer and inner Publishers are + *
Backpressure is honored towards the downstream and the inner {@code Publisher}s are * expected to support backpressure. Violating this assumption, the operator will - * signal {@code MissingBackpressureException}.
+ * signal {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Publishers that need to be eagerly concatenated - * @return the new Publisher instance with the specified concatenation behavior + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatEager(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable concatEager(@NonNull Iterable<@NonNull ? extends Publisher> sources) { return concatEager(sources, bufferSize(), bufferSize()); } /** - * Concatenates a Publisher sequence of Publishers eagerly into a single stream of values. + * Concatenates a sequence of {@link Publisher}s eagerly into a single stream of values and + * runs a limited number of inner sequences at once. + *

+ * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * emitted source Publishers as they are observed. The operator buffers the values emitted by these - * Publishers and then drains them in order, each one after the previous one completes. + * source {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them + * in order, each one after the previous one completes. *

*
Backpressure:
- *
Backpressure is honored towards the downstream and both the outer and inner Publishers are + *
Backpressure is honored towards the downstream and both the outer and inner {@code Publisher}s are * expected to support backpressure. Violating this assumption, the operator will - * signal {@code MissingBackpressureException}.
+ * signal {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Publishers that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrently running inner Publishers; Integer.MAX_VALUE - * is interpreted as all inner Publishers can be active at the same time - * @param prefetch the number of elements to prefetch from each inner Publisher source - * @return the new Publisher instance with the specified concatenation behavior + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code Publisher}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code Publisher}s can be active at the same time + * @param prefetch the number of elements to prefetch from each inner {@code Publisher} source + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive * @since 2.0 */ @CheckReturnValue @@ -1642,59 +1727,68 @@ public static Flowable concatEager(Publisher Flowable concatEager(Publisher> sources, int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Flowable concatEager(@NonNull Iterable<@NonNull ? extends Publisher> sources, int maxConcurrency, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapEagerPublisher(sources, Functions.identity(), maxConcurrency, prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapEager(new FlowableFromIterable(sources), Functions.identity(), maxConcurrency, prefetch, ErrorMode.BOUNDARY)); } /** - * Concatenates a sequence of Publishers eagerly into a single stream of values. + * Concatenates a {@link Publisher} sequence of {@code Publisher}s eagerly into a single stream of values. + *

+ * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them - * in order, each one after the previous one completes. + * emitted source {@code Publisher}s as they are observed. The operator buffers the values emitted by these + * {@code Publisher}s and then drains them in order, each one after the previous one completes. *

*
Backpressure:
- *
Backpressure is honored towards the downstream and the inner Publishers are + *
Backpressure is honored towards the downstream and both the outer and inner {@code Publisher}s are * expected to support backpressure. Violating this assumption, the operator will - * signal {@code MissingBackpressureException}.
+ * signal {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Publishers that need to be eagerly concatenated - * @return the new Publisher instance with the specified concatenation behavior + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatEager(Iterable> sources) { + @NonNull + public static <@NonNull T> Flowable concatEager(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return concatEager(sources, bufferSize(), bufferSize()); } /** - * Concatenates a sequence of Publishers eagerly into a single stream of values. + * Concatenates a {@link Publisher} sequence of {@code Publisher}s eagerly into a single stream of values and + * runs a limited number of inner sequences at once. + *

+ * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them - * in order, each one after the previous one completes. + * emitted source {@code Publisher}s as they are observed. The operator buffers the values emitted by these + * {@code Publisher}s and then drains them in order, each one after the previous one completes. *

*
Backpressure:
- *
Backpressure is honored towards the downstream and both the outer and inner Publishers are + *
Backpressure is honored towards the downstream and both the outer and inner {@code Publisher}s are * expected to support backpressure. Violating this assumption, the operator will - * signal {@code MissingBackpressureException}.
+ * signal {@link MissingBackpressureException}. *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Publishers that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrently running inner Publishers; Integer.MAX_VALUE - * is interpreted as all inner Publishers can be active at the same time - * @param prefetch the number of elements to prefetch from each inner Publisher source - * @return the new Publisher instance with the specified concatenation behavior + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code Publisher}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code Publisher}s can be active at the same time + * @param prefetch the number of elements to prefetch from each inner {@code Publisher} source + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive * @since 2.0 */ @CheckReturnValue @@ -1702,15 +1796,157 @@ public static Flowable concatEager(Iterable Flowable concatEager(Iterable> sources, int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Flowable concatEager(@NonNull Publisher<@NonNull ? extends Publisher> sources, int maxConcurrency, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); + ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); + ObjectHelper.verifyPositive(prefetch, "prefetch"); + return RxJavaPlugins.onAssembly(new FlowableConcatMapEagerPublisher(sources, Functions.identity(), maxConcurrency, prefetch, ErrorMode.IMMEDIATE)); + } + + /** + * Concatenates a sequence of {@link Publisher}s eagerly into a single stream of values, + * delaying errors until all the inner sequences terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * source {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them + * in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the inner {@code Publisher}s are + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources) { + return concatEagerDelayError(sources, bufferSize(), bufferSize()); + } + + /** + * Concatenates a sequence of {@link Publisher}s eagerly into a single stream of values, + * delaying errors until all the inner sequences terminate and runs a limited number + * of inner sequences at once. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * source {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them + * in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and both the outer and inner {@code Publisher}s are + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code Publisher}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code Publisher}s can be active at the same time + * @param prefetch the number of elements to prefetch from each inner {@code Publisher} source + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources, int maxConcurrency, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); + ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); + ObjectHelper.verifyPositive(prefetch, "prefetch"); + return RxJavaPlugins.onAssembly(new FlowableConcatMapEager(new FlowableFromIterable(sources), Functions.identity(), maxConcurrency, prefetch, ErrorMode.END)); + } + + /** + * Concatenates a {@link Publisher} sequence of {@code Publisher}s eagerly into a single stream of values, + * delaying errors until all the inner and the outer sequences terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code Publisher}s as they are observed. The operator buffers the values emitted by these + * {@code Publisher}s and then drains them in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and both the outer and inner {@code Publisher}s are + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources) { + return concatEagerDelayError(sources, bufferSize(), bufferSize()); + } + + /** + * Concatenates a {@link Publisher} sequence of {@code Publisher}s eagerly into a single stream of values, + * delaying errors until all the inner and outer sequences terminate and runs a limited number of inner + * sequences at once. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code Publisher}s as they are observed. The operator buffers the values emitted by these + * {@code Publisher}s and then drains them in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and both the outer and inner {@code Publisher}s are + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code Publisher}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code Publisher}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code Publisher}s can be active at the same time + * @param prefetch the number of elements to prefetch from each inner {@code Publisher} source + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources, int maxConcurrency, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapEager(new FlowableFromIterable(sources), Functions.identity(), maxConcurrency, prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapEagerPublisher(sources, Functions.identity(), maxConcurrency, prefetch, ErrorMode.END)); } /** - * Provides an API (via a cold Flowable) that bridges the reactive world with the callback-style, + * Provides an API (via a cold {@code Flowable}) that bridges the reactive world with the callback-style, * generally non-backpressured world. *

* Example: @@ -1741,10 +1977,11 @@ public static Flowable concatEager(Iterable - * You should call the FlowableEmitter onNext, onError and onComplete methods in a serialized fashion. The + * You should call the {@link FlowableEmitter#onNext(Object)}, {@link FlowableEmitter#onError(Throwable)} + * and {@link FlowableEmitter#onComplete()} methods in a serialized fashion. The * rest of its methods are thread-safe. *

*
Backpressure:
@@ -1754,9 +1991,10 @@ public static Flowable concatEager(Iterable * * @param the element type - * @param source the emitter that is called when a Subscriber subscribes to the returned {@code Flowable} - * @param mode the backpressure mode to apply if the downstream Subscriber doesn't request (fast) enough - * @return the new Flowable instance + * @param source the emitter that is called when a {@code Subscriber} subscribes to the returned {@code Flowable} + * @param mode the backpressure mode to apply if the downstream {@code Subscriber} doesn't request (fast) enough + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source} or {@code mode} is {@code null} * @see FlowableOnSubscribe * @see BackpressureStrategy * @see Cancellable @@ -1765,21 +2003,21 @@ public static Flowable concatEager(Iterable Flowable create(FlowableOnSubscribe source, BackpressureStrategy mode) { - ObjectHelper.requireNonNull(source, "source is null"); - ObjectHelper.requireNonNull(mode, "mode is null"); - return RxJavaPlugins.onAssembly(new FlowableCreate(source, mode)); + public static <@NonNull T> Flowable create(@NonNull FlowableOnSubscribe source, @NonNull BackpressureStrategy mode) { + Objects.requireNonNull(source, "source is null"); + Objects.requireNonNull(mode, "mode is null"); + return RxJavaPlugins.onAssembly(new FlowableCreate<>(source, mode)); } /** - * Returns a Flowable that calls a Publisher factory to create a Publisher for each new Subscriber - * that subscribes. That is, for each subscriber, the actual Publisher that subscriber observes is + * Returns a {@code Flowable} that calls a {@link Publisher} factory to create a {@code Publisher} for each new {@link Subscriber} + * that subscribes. That is, for each subscriber, the actual {@code Publisher} that subscriber observes is * determined by the factory function. *

- * + * *

- * The defer Subscriber allows you to defer or delay emitting items from a Publisher until such time as a - * Subscriber subscribes to the Publisher. This allows a {@link Subscriber} to easily obtain updates or a + * The defer {@code Subscriber} allows you to defer or delay emitting items from a {@code Publisher} until such time as a + * {@code Subscriber} subscribes to the {@code Publisher}. This allows a {@code Subscriber} to easily obtain updates or a * refreshed version of the sequence. *

*
Backpressure:
@@ -1790,28 +2028,28 @@ public static Flowable create(FlowableOnSubscribe source, Backpressure *
* * @param supplier - * the Publisher factory function to invoke for each {@link Subscriber} that subscribes to the - * resulting Publisher + * the {@code Publisher} factory function to invoke for each {@code Subscriber} that subscribes to the + * resulting {@code Flowable} * @param - * the type of the items emitted by the Publisher - * @return a Flowable whose {@link Subscriber}s' subscriptions trigger an invocation of the given - * Publisher factory function + * the type of the items emitted by the {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see ReactiveX operators documentation: Defer */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable defer(Supplier> supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new FlowableDefer(supplier)); + public static <@NonNull T> Flowable defer(@NonNull Supplier> supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new FlowableDefer<>(supplier)); } /** - * Returns a Flowable that emits no items to the {@link Subscriber} and immediately invokes its + * Returns a {@code Flowable} that emits no items to the {@link Subscriber} and immediately invokes its * {@link Subscriber#onComplete onComplete} method. *

- * + * *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -1820,24 +2058,24 @@ public static Flowable defer(Supplier> s *
* * @param - * the type of the items (ostensibly) emitted by the Publisher - * @return a Flowable that emits no items to the {@link Subscriber} but immediately invokes the - * {@link Subscriber}'s {@link Subscriber#onComplete() onComplete} method + * the type of the items (ostensibly) emitted by the {@link Publisher} + * @return the shared {@code Flowable} instance * @see ReactiveX operators documentation: Empty */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Flowable empty() { + @NonNull + public static <@NonNull T> Flowable empty() { return RxJavaPlugins.onAssembly((Flowable) FlowableEmpty.INSTANCE); } /** - * Returns a Flowable that invokes a {@link Subscriber}'s {@link Subscriber#onError onError} method when the - * Subscriber subscribes to it. + * Returns a {@code Flowable} that invokes a {@link Subscriber}'s {@link Subscriber#onError onError} method when the + * {@code Subscriber} subscribes to it. *

- * + * *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -1846,27 +2084,27 @@ public static Flowable empty() { *
* * @param supplier - * a Supplier factory to return a Throwable for each individual Subscriber + * a {@link Supplier} factory to return a {@link Throwable} for each individual {@code Subscriber} * @param - * the type of the items (ostensibly) emitted by the Publisher - * @return a Flowable that invokes the {@link Subscriber}'s {@link Subscriber#onError onError} method when - * the Subscriber subscribes to it + * the type of the items (ostensibly) emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable error(Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new FlowableError(supplier)); + public static <@NonNull T> Flowable error(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new FlowableError<>(supplier)); } /** - * Returns a Flowable that invokes a {@link Subscriber}'s {@link Subscriber#onError onError} method when the - * Subscriber subscribes to it. + * Returns a {@code Flowable} that invokes a {@link Subscriber}'s {@link Subscriber#onError onError} method when the + * {@code Subscriber} subscribes to it. *

- * + * *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -1875,26 +2113,59 @@ public static Flowable error(Supplier supplier) { *
* * @param throwable - * the particular Throwable to pass to {@link Subscriber#onError onError} + * the particular {@link Throwable} to pass to {@link Subscriber#onError onError} * @param - * the type of the items (ostensibly) emitted by the Publisher - * @return a Flowable that invokes the {@link Subscriber}'s {@link Subscriber#onError onError} method when - * the Subscriber subscribes to it + * the type of the items (ostensibly) emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code throwable} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable error(final Throwable throwable) { - ObjectHelper.requireNonNull(throwable, "throwable is null"); + public static <@NonNull T> Flowable error(@NonNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is null"); return error(Functions.justSupplier(throwable)); } /** - * Converts an Array into a Publisher that emits the items in the Array. + * Returns a {@code Flowable} instance that runs the given {@link Action} for each {@link Subscriber} and + * emits either its exception or simply completes. + *

+ * + *

+ *
Backpressure:
+ *
This source doesn't produce any elements and effectively ignores downstream backpressure.
+ *
Scheduler:
+ *
{@code fromAction} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the {@code Action} throws an exception, the respective {@link Throwable} is + * delivered to the downstream via {@link Subscriber#onError(Throwable)}, + * except when the downstream has canceled the resulting {@code Flowable} source. + * In this latter case, the {@code Throwable} is delivered to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + *
+ *
+ * @param the target type + * @param action the {@code Action} to run for each {@code Subscriber} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code action} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public static <@NonNull T> Flowable fromAction(@NonNull Action action) { + Objects.requireNonNull(action, "action is null"); + return RxJavaPlugins.onAssembly(new FlowableFromAction<>(action)); + } + + /** + * Converts an array into a {@link Publisher} that emits the items in the array. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and iterates the given {@code array} @@ -1906,33 +2177,35 @@ public static Flowable error(final Throwable throwable) { * @param items * the array of elements * @param - * the type of items in the Array and the type of items to be emitted by the resulting Publisher - * @return a Flowable that emits each item in the source Array + * the type of items in the array and the type of items to be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code items} is {@code null} * @see ReactiveX operators documentation: From */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable fromArray(T... items) { - ObjectHelper.requireNonNull(items, "items is null"); + @SafeVarargs + public static <@NonNull T> Flowable fromArray(@NonNull T... items) { + Objects.requireNonNull(items, "items is null"); if (items.length == 0) { return empty(); } if (items.length == 1) { return just(items[0]); } - return RxJavaPlugins.onAssembly(new FlowableFromArray(items)); + return RxJavaPlugins.onAssembly(new FlowableFromArray<>(items)); } /** - * Returns a Flowable that, when a Subscriber subscribes to it, invokes a function you specify and then + * Returns a {@code Flowable} that, when a {@link Subscriber} subscribes to it, invokes a function you specify and then * emits the value returned from that function. *

- * + * *

- * This allows you to defer the execution of the function you specify until a Subscriber subscribes to the - * Publisher. That is to say, it makes the function "lazy." + * This allows you to defer the execution of the function you specify until a {@code Subscriber} subscribes to the + * {@link Publisher}. That is to say, it makes the function "lazy." *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -1947,12 +2220,13 @@ public static Flowable fromArray(T... items) { *
*
* - * @param supplier + * @param callable * a function, the execution of which should be deferred; {@code fromCallable} will invoke this - * function only when a Subscriber subscribes to the Publisher that {@code fromCallable} returns + * function only when a {@code Subscriber} subscribes to the {@code Publisher} that {@code fromCallable} returns * @param - * the type of the item emitted by the Publisher - * @return a Flowable whose {@link Subscriber}s' subscriptions trigger an invocation of the given function + * the type of the item emitted by the {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code callable} is {@code null} * @see #defer(Supplier) * @see #fromSupplier(Supplier) * @since 2.0 @@ -1961,215 +2235,255 @@ public static Flowable fromArray(T... items) { @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable fromCallable(Callable supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new FlowableFromCallable(supplier)); + public static <@NonNull T> Flowable fromCallable(@NonNull Callable callable) { + Objects.requireNonNull(callable, "callable is null"); + return RxJavaPlugins.onAssembly(new FlowableFromCallable<>(callable)); + } + + /** + * Wraps a {@link CompletableSource} into a {@code Flowable}. + *

+ * + *

+ *
Backpressure:
+ *
This source doesn't produce any elements and effectively ignores downstream backpressure.
+ *
Scheduler:
+ *
{@code fromCompletable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the target type + * @param completableSource the {@code CompletableSource} to convert from + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code completableSource} is {@code null} + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public static <@NonNull T> Flowable fromCompletable(@NonNull CompletableSource completableSource) { + Objects.requireNonNull(completableSource, "completableSource is null"); + return RxJavaPlugins.onAssembly(new FlowableFromCompletable<>(completableSource)); } /** - * Converts a {@link Future} into a Publisher. + * Converts a {@link Future} into a {@link Publisher}. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into a Publisher that emits the - * return value of the {@link Future#get} method of that object by passing the object into the {@code from} - * method. + * The operator calls {@link Future#get()}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Important note: This Publisher is blocking on the thread it gets subscribed on; you cannot cancel it. + * Also note that this operator will consume a {@link CompletionStage}-based {@code Future} subclass (such as + * {@link CompletableFuture}) in a blocking manner as well. Use the {@link #fromCompletionStage(CompletionStage)} + * operator to convert and consume such sources in a non-blocking fashion instead. *

- * Unlike 1.x, canceling the Flowable won't cancel the future. If necessary, one can use composition to achieve the + * Unlike 1.x, canceling the {@code Flowable} won't cancel the future. If necessary, one can use composition to achieve the * cancellation effect: {@code futurePublisher.doOnCancel(() -> future.cancel(true));}. *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting Publisher - * @return a Flowable that emits the item from the source {@link Future} + * the type of object that the {@code Future} returns, and also the type of item to be emitted by + * the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code future} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable fromFuture(Future future) { - ObjectHelper.requireNonNull(future, "future is null"); - return RxJavaPlugins.onAssembly(new FlowableFromFuture(future, 0L, null)); + public static <@NonNull T> Flowable fromFuture(@NonNull Future future) { + Objects.requireNonNull(future, "future is null"); + return RxJavaPlugins.onAssembly(new FlowableFromFuture<>(future, 0L, null)); } /** - * Converts a {@link Future} into a Publisher, with a timeout on the Future. + * Converts a {@link Future} into a {@link Publisher}, with a timeout on the {@code Future}. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into a Publisher that emits the - * return value of the {@link Future#get} method of that object by passing the object into the {@code fromFuture} - * method. + * The operator calls {@link Future#get(long, TimeUnit)}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Unlike 1.x, canceling the Flowable won't cancel the future. If necessary, one can use composition to achieve the + * Unlike 1.x, canceling the {@code Flowable} won't cancel the future. If necessary, one can use composition to achieve the * cancellation effect: {@code futurePublisher.doOnCancel(() -> future.cancel(true));}. *

- * Important note: This Publisher is blocking on the thread it gets subscribed on; you cannot cancel it. + * Also note that this operator will consume a {@link CompletionStage}-based {@code Future} subclass (such as + * {@link CompletableFuture}) in a blocking manner as well. Use the {@link #fromCompletionStage(CompletionStage)} + * operator to convert and consume such sources in a non-blocking fashion instead. *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param timeout * the maximum time to wait before calling {@code get} * @param unit * the {@link TimeUnit} of the {@code timeout} argument * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting Publisher - * @return a Flowable that emits the item from the source {@link Future} + * the type of object that the {@code Future} returns, and also the type of item to be emitted by + * the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code future} or {@code unit} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable fromFuture(Future future, long timeout, TimeUnit unit) { - ObjectHelper.requireNonNull(future, "future is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - return RxJavaPlugins.onAssembly(new FlowableFromFuture(future, timeout, unit)); + public static <@NonNull T> Flowable fromFuture(@NonNull Future future, long timeout, @NonNull TimeUnit unit) { + Objects.requireNonNull(future, "future is null"); + Objects.requireNonNull(unit, "unit is null"); + return RxJavaPlugins.onAssembly(new FlowableFromFuture<>(future, timeout, unit)); } /** - * Converts a {@link Future} into a Publisher, with a timeout on the Future. - *

- * - *

- * You can convert any object that supports the {@link Future} interface into a Publisher that emits the - * return value of the {@link Future#get} method of that object by passing the object into the {@code from} - * method. + * Converts an {@link Iterable} sequence into a {@link Publisher} that emits the items in the sequence. *

- * Unlike 1.x, canceling the Flowable won't cancel the future. If necessary, one can use composition to achieve the - * cancellation effect: {@code futurePublisher.doOnCancel(() -> future.cancel(true));}. - *

- * Important note: This Publisher is blocking; you cannot cancel it. + * *

*
Backpressure:
- *
The operator honors backpressure from downstream.
+ *
The operator honors backpressure from downstream and iterates the given {@code iterable} + * on demand (i.e., when requested).
*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromIterable} does not operate by default on a particular {@link Scheduler}.
*
* - * @param future - * the source {@link Future} - * @param timeout - * the maximum time to wait before calling {@code get} - * @param unit - * the {@link TimeUnit} of the {@code timeout} argument - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as - * {@link Schedulers#io()} that can block and wait on the Future + * @param source + * the source {@code Iterable} sequence * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting Publisher - * @return a Flowable that emits the item from the source {@link Future} + * the type of items in the {@code Iterable} sequence and the type of items to be emitted by the + * resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromStream(Stream) */ - @SuppressWarnings({ "unchecked", "cast" }) @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) - @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Flowable fromFuture(Future future, long timeout, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return fromFuture((Future)future, timeout, unit).subscribeOn(scheduler); + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable fromIterable(@NonNull Iterable source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new FlowableFromIterable<>(source)); } /** - * Converts a {@link Future}, operating on a specified {@link Scheduler}, into a Publisher. - *

- * + * Returns a {@code Flowable} instance that when subscribed to, subscribes to the {@link MaybeSource} instance and + * emits {@code onSuccess} as a single item or forwards any {@code onComplete} or + * {@code onError} signal. *

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

- * Unlike 1.x, canceling the Flowable won't cancel the future. If necessary, one can use composition to achieve the - * cancellation effect: {@code futurePublisher.doOnCancel(() -> future.cancel(true));}. + * *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
{@code fromMaybe} does not operate by default on a particular {@link Scheduler}.
*
- * - * @param future - * the source {@link Future} - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as - * {@link Schedulers#io()} that can block and wait on the Future - * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting Publisher - * @return a Flowable that emits the item from the source {@link Future} - * @see ReactiveX operators documentation: From + * @param the value type of the {@code MaybeSource} element + * @param maybe the {@code MaybeSource} instance to subscribe to, not {@code null} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code maybe} is {@code null} + * @since 3.0.0 */ - @SuppressWarnings({ "cast", "unchecked" }) @CheckReturnValue @NonNull + @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Flowable fromFuture(Future future, Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return fromFuture((Future)future).subscribeOn(scheduler); + public static <@NonNull T> Flowable fromMaybe(@NonNull MaybeSource maybe) { + Objects.requireNonNull(maybe, "maybe is null"); + return RxJavaPlugins.onAssembly(new MaybeToFlowable<>(maybe)); } /** - * Converts an {@link Iterable} sequence into a Publisher that emits the items in the sequence. + * Converts the given {@link ObservableSource} into a {@code Flowable} by applying the specified backpressure strategy. + *

+ * Marble diagrams for the various backpressure strategies are as follows: + *

    + *
  • {@link BackpressureStrategy#BUFFER} + *

    + * + *

  • + *
  • {@link BackpressureStrategy#DROP} + *

    + * + *

  • + *
  • {@link BackpressureStrategy#LATEST} + *

    + * + *

  • + *
  • {@link BackpressureStrategy#ERROR} *

    - * + * + *

  • + *
  • {@link BackpressureStrategy#MISSING} + *

    + * + *

  • + *
*
*
Backpressure:
- *
The operator honors backpressure from downstream and iterates the given {@code iterable} - * on demand (i.e., when requested).
+ *
The operator applies the chosen backpressure strategy of {@link BackpressureStrategy} enum.
*
Scheduler:
- *
{@code fromIterable} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromObservable} does not operate by default on a particular {@link Scheduler}.
*
* - * @param source - * the source {@link Iterable} sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be emitted by the - * resulting Publisher - * @return a Flowable that emits each item in the source {@link Iterable} sequence - * @see ReactiveX operators documentation: From + * @param the element type of the source and resulting sequence + * @param source the {@code ObservableSource} to convert + * @param strategy the backpressure strategy to apply + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source} or {@code strategy} is {@code null} */ + @BackpressureSupport(BackpressureKind.SPECIAL) @CheckReturnValue - @NonNull - @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable fromIterable(Iterable source) { - ObjectHelper.requireNonNull(source, "source is null"); - return RxJavaPlugins.onAssembly(new FlowableFromIterable(source)); + @NonNull + public static <@NonNull T> Flowable fromObservable(@NonNull ObservableSource source, @NonNull BackpressureStrategy strategy) { + Objects.requireNonNull(source, "source is null"); + Objects.requireNonNull(strategy, "strategy is null"); + Flowable f = new FlowableFromObservable<>(source); + switch (strategy) { + case DROP: + return f.onBackpressureDrop(); + case LATEST: + return f.onBackpressureLatest(); + case MISSING: + return f; + case ERROR: + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureError<>(f)); + default: + return f.onBackpressureBuffer(); + } } /** - * Converts an arbitrary Reactive Streams Publisher into a Flowable if not already a - * Flowable. + * Converts an arbitrary Reactive Streams {@link Publisher} into a {@code Flowable} if not already a + * {@code Flowable}. *

- * The {@link Publisher} must follow the + * The {@code Publisher} must follow the * Reactive-Streams specification. * Violating the specification may result in undefined behavior. *

* If possible, use {@link #create(FlowableOnSubscribe, BackpressureStrategy)} to create a * source-like {@code Flowable} instead. *

- * Note that even though {@link Publisher} appears to be a functional interface, it + * Note that even though {@code Publisher} appears to be a functional interface, it * is not recommended to implement it through a lambda as the specification requires * state management that is not achievable with a stateless lambda. *

@@ -2180,9 +2494,9 @@ public static Flowable fromIterable(Iterable source) { *
{@code fromPublisher} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type of the flow - * @param source the Publisher to convert - * @return the new Flowable instance - * @throws NullPointerException if the {@code source} {@code Publisher} is null + * @param publisher the {@code Publisher} to convert + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code publisher} is {@code null} * @see #create(FlowableOnSubscribe, BackpressureStrategy) */ @CheckReturnValue @@ -2190,23 +2504,88 @@ public static Flowable fromIterable(Iterable source) { @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Flowable fromPublisher(final Publisher source) { - if (source instanceof Flowable) { - return RxJavaPlugins.onAssembly((Flowable)source); + public static <@NonNull T> Flowable fromPublisher(@NonNull Publisher publisher) { + if (publisher instanceof Flowable) { + return RxJavaPlugins.onAssembly((Flowable)publisher); } - ObjectHelper.requireNonNull(source, "source is null"); + Objects.requireNonNull(publisher, "publisher is null"); + + return RxJavaPlugins.onAssembly(new FlowableFromPublisher<>(publisher)); + } + + /** + * Returns a {@code Flowable} instance that runs the given {@link Runnable} for each {@link Subscriber} and + * emits either its unchecked exception or simply completes. + *

+ * + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. + *

+ *
Backpressure:
+ *
This source doesn't produce any elements and effectively ignores downstream backpressure.
+ *
Scheduler:
+ *
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is + * delivered to the downstream via {@link Subscriber#onError(Throwable)}, + * except when the downstream has canceled the resulting {@code Flowable} source. + * In this latter case, the {@code Throwable} is delivered to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + *
+ *
+ * @param the target type + * @param run the {@code Runnable} to run for each {@code Subscriber} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code run} is {@code null} + * @since 3.0.0 + * @see #fromAction(Action) + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public static <@NonNull T> Flowable fromRunnable(@NonNull Runnable run) { + Objects.requireNonNull(run, "run is null"); + return RxJavaPlugins.onAssembly(new FlowableFromRunnable<>(run)); + } - return RxJavaPlugins.onAssembly(new FlowableFromPublisher(source)); + /** + * Returns a {@code Flowable} instance that when subscribed to, subscribes to the {@link SingleSource} instance and + * emits {@code onSuccess} as a single item or forwards the {@code onError} signal. + *

+ * + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream.
+ *
Scheduler:
+ *
{@code fromSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type of the {@code SingleSource} element + * @param source the {@code SingleSource} instance to subscribe to, not {@code null} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public static <@NonNull T> Flowable fromSingle(@NonNull SingleSource source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new SingleToFlowable<>(source)); } /** - * Returns a Flowable that, when a Subscriber subscribes to it, invokes a supplier function you specify and then + * Returns a {@code Flowable} that, when a {@link Subscriber} subscribes to it, invokes a supplier function you specify and then * emits the value returned from that function. *

- * + * *

- * This allows you to defer the execution of the function you specify until a Subscriber subscribes to the - * Publisher. That is to say, it makes the function "lazy." + * This allows you to defer the execution of the function you specify until a {@code Subscriber} subscribes to the + * {@link Publisher}. That is to say, it makes the function "lazy." *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -2223,10 +2602,11 @@ public static Flowable fromPublisher(final Publisher source) * * @param supplier * a function, the execution of which should be deferred; {@code fromSupplier} will invoke this - * function only when a Subscriber subscribes to the Publisher that {@code fromSupplier} returns + * function only when a {@code Subscriber} subscribes to the {@code Publisher} that {@code fromSupplier} returns * @param - * the type of the item emitted by the Publisher - * @return a Flowable whose {@link Subscriber}s' subscriptions trigger an invocation of the given function + * the type of the item emitted by the {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see #defer(Supplier) * @see #fromCallable(Callable) * @since 3.0.0 @@ -2235,9 +2615,9 @@ public static Flowable fromPublisher(final Publisher source) @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable fromSupplier(Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new FlowableFromSupplier(supplier)); + public static <@NonNull T> Flowable fromSupplier(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new FlowableFromSupplier<>(supplier)); } /** @@ -2255,20 +2635,21 @@ public static Flowable fromSupplier(Supplier supplier) { *
* * @param the generated value type - * @param generator the Consumer called whenever a particular downstream Subscriber has + * @param generator the {@link Consumer} called whenever a particular downstream {@link Subscriber} has * requested a value. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event. Signaling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @return the new Flowable instance + * in a call will make the operator signal {@link IllegalStateException}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code generator} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable generate(final Consumer> generator) { - ObjectHelper.requireNonNull(generator, "generator is null"); + public static <@NonNull T> Flowable generate(@NonNull Consumer<@NonNull Emitter> generator) { + Objects.requireNonNull(generator, "generator is null"); return generate(Functions.nullSupplier(), - FlowableInternalHelper.simpleGenerator(generator), + FlowableInternalHelper.simpleGenerator(generator), Functions.emptyConsumer()); } @@ -2286,22 +2667,23 @@ public static Flowable generate(final Consumer> generator) { *
{@code generate} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the type of the per-Subscriber state + * @param the type of the per-{@link Subscriber} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Subscriber - * @param generator the Consumer called with the current state whenever a particular downstream Subscriber has + * @param initialState the {@link Supplier} to generate the initial state for each {@code Subscriber} + * @param generator the {@link Consumer} called with the current state whenever a particular downstream {@code Subscriber} has * requested a value. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event. Signaling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @return the new Flowable instance + * in a call will make the operator signal {@link IllegalStateException}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code initialState} or {@code generator} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable generate(Supplier initialState, final BiConsumer> generator) { - ObjectHelper.requireNonNull(generator, "generator is null"); - return generate(initialState, FlowableInternalHelper.simpleBiGenerator(generator), + public static <@NonNull T, @NonNull S> Flowable generate(@NonNull Supplier initialState, @NonNull BiConsumer> generator) { + Objects.requireNonNull(generator, "generator is null"); + return generate(initialState, FlowableInternalHelper.simpleBiGenerator(generator), Functions.emptyConsumer()); } @@ -2319,25 +2701,26 @@ public static Flowable generate(Supplier initialState, final BiCons *
{@code generate} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the type of the per-Subscriber state + * @param the type of the per-{@link Subscriber} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Subscriber - * @param generator the Consumer called with the current state whenever a particular downstream Subscriber has + * @param initialState the {@link Supplier} to generate the initial state for each {@code Subscriber} + * @param generator the {@link Consumer} called with the current state whenever a particular downstream {@code Subscriber} has * requested a value. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event. Signaling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @param disposeState the Consumer that is called with the current state when the generator + * in a call will make the operator signal {@link IllegalStateException}. + * @param disposeState the {@code Consumer} that is called with the current state when the generator * terminates the sequence or it gets canceled - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code initialState}, {@code generator} or {@code disposeState} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable generate(Supplier initialState, final BiConsumer> generator, - Consumer disposeState) { - ObjectHelper.requireNonNull(generator, "generator is null"); - return generate(initialState, FlowableInternalHelper.simpleBiGenerator(generator), disposeState); + public static <@NonNull T, @NonNull S> Flowable generate(@NonNull Supplier initialState, @NonNull BiConsumer> generator, + @NonNull Consumer disposeState) { + Objects.requireNonNull(generator, "generator is null"); + return generate(initialState, FlowableInternalHelper.simpleBiGenerator(generator), disposeState); } /** @@ -2354,20 +2737,22 @@ public static Flowable generate(Supplier initialState, final BiCons *
{@code generate} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the type of the per-Subscriber state + * @param the type of the per-{@link Subscriber} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Subscriber - * @param generator the Function called with the current state whenever a particular downstream Subscriber has + * @param initialState the {@link Supplier} to generate the initial state for each {@code Subscriber} + * @param generator the {@link Function} called with the current state whenever a particular downstream {@code Subscriber} has * requested a value. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event and should return a (new) state for * the next invocation. Signaling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @return the new Flowable instance + * in a call will make the operator signal {@link IllegalStateException}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code initialState} or {@code generator} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable generate(Supplier initialState, BiFunction, S> generator) { + @NonNull + public static <@NonNull T, @NonNull S> Flowable generate(@NonNull Supplier initialState, @NonNull BiFunction, S> generator) { return generate(initialState, generator, Functions.emptyConsumer()); } @@ -2385,39 +2770,40 @@ public static Flowable generate(Supplier initialState, BiFunction{@code generate} does not operate by default on a particular {@link Scheduler}. * * - * @param the type of the per-Subscriber state + * @param the type of the per-{@link Subscriber} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Subscriber - * @param generator the Function called with the current state whenever a particular downstream Subscriber has + * @param initialState the {@link Supplier} to generate the initial state for each {@code Subscriber} + * @param generator the {@link Function} called with the current state whenever a particular downstream {@code Subscriber} has * requested a value. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event and should return a (new) state for * the next invocation. Signaling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @param disposeState the Consumer that is called with the current state when the generator + * in a call will make the operator signal {@link IllegalStateException}. + * @param disposeState the {@link Consumer} that is called with the current state when the generator * terminates the sequence or it gets canceled - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code initialState}, {@code generator} or {@code disposeState} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable generate(Supplier initialState, BiFunction, S> generator, Consumer disposeState) { - ObjectHelper.requireNonNull(initialState, "initialState is null"); - ObjectHelper.requireNonNull(generator, "generator is null"); - ObjectHelper.requireNonNull(disposeState, "disposeState is null"); - return RxJavaPlugins.onAssembly(new FlowableGenerate(initialState, generator, disposeState)); + public static <@NonNull T, @NonNull S> Flowable generate(@NonNull Supplier initialState, @NonNull BiFunction, S> generator, @NonNull Consumer disposeState) { + Objects.requireNonNull(initialState, "initialState is null"); + Objects.requireNonNull(generator, "generator is null"); + Objects.requireNonNull(disposeState, "disposeState is null"); + return RxJavaPlugins.onAssembly(new FlowableGenerate<>(initialState, generator, disposeState)); } /** - * Returns a Flowable that emits a {@code 0L} after the {@code initialDelay} and ever-increasing numbers + * Returns a {@code Flowable} that emits a {@code 0L} after the {@code initialDelay} and ever-increasing numbers * after each {@code period} of time thereafter. *

- * + * *

*
Backpressure:
*
The operator generates values based on time and ignores downstream backpressure which - * may lead to {@code MissingBackpressureException} at some point in the chain. - * Consumers should consider applying one of the {@code onBackpressureXXX} operators as well.
+ * may lead to {@link MissingBackpressureException} at some point in the chain. + * Downstream consumers should consider applying one of the {@code onBackpressureXXX} operators as well. *
Scheduler:
*
{@code interval} operates by default on the {@code computation} {@link Scheduler}.
*
@@ -2428,30 +2814,31 @@ public static Flowable generate(Supplier initialState, BiFunctionReactiveX operators documentation: Interval * @since 1.0.12 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Flowable interval(long initialDelay, long period, TimeUnit unit) { + @NonNull + public static Flowable interval(long initialDelay, long period, @NonNull TimeUnit unit) { return interval(initialDelay, period, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits a {@code 0L} after the {@code initialDelay} and ever-increasing numbers + * Returns a {@code Flowable} that emits a {@code 0L} after the {@code initialDelay} and ever-increasing numbers * after each {@code period} of time thereafter, on a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator generates values based on time and ignores downstream backpressure which - * may lead to {@code MissingBackpressureException} at some point in the chain. - * Consumers should consider applying one of the {@code onBackpressureXXX} operators as well.
+ * may lead to {@link MissingBackpressureException} at some point in the chain. + * Downstream consumers should consider applying one of the {@code onBackpressureXXX} operators as well. *
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param initialDelay @@ -2461,9 +2848,9 @@ public static Flowable interval(long initialDelay, long period, TimeUnit u * @param unit * the time unit for both {@code initialDelay} and {@code period} * @param scheduler - * the Scheduler on which the waiting happens and items are emitted - * @return a Flowable that emits a 0L after the {@code initialDelay} and ever-increasing numbers after - * each {@code period} of time thereafter, while running on the given Scheduler + * the {@code Scheduler} on which the waiting happens and items are emitted + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Interval * @since 1.0.12 */ @@ -2471,19 +2858,19 @@ public static Flowable interval(long initialDelay, long period, TimeUnit u @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Flowable interval(long initialDelay, long period, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public static Flowable interval(long initialDelay, long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new FlowableInterval(Math.max(0L, initialDelay), Math.max(0L, period), unit, scheduler)); } /** - * Returns a Flowable that emits a sequential number every specified interval of time. + * Returns a {@code Flowable} that emits a sequential number every specified interval of time. *

- * + * *

*
Backpressure:
- *
The operator signals a {@code MissingBackpressureException} if the downstream + *
The operator signals a {@link MissingBackpressureException} if the downstream * is not ready to receive the next value.
*
Scheduler:
*
{@code interval} operates by default on the {@code computation} {@link Scheduler}.
@@ -2493,28 +2880,30 @@ public static Flowable interval(long initialDelay, long period, TimeUnit u * the period size in time units (see below) * @param unit * time units to use for the interval size - * @return a Flowable that emits a sequential number each time interval + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Interval */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Flowable interval(long period, TimeUnit unit) { + @NonNull + public static Flowable interval(long period, @NonNull TimeUnit unit) { return interval(period, period, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits a sequential number every specified interval of time, on a - * specified Scheduler. + * Returns a {@code Flowable} that emits a sequential number every specified interval of time, on a + * specified {@link Scheduler}. *

- * + * *

*
Backpressure:
*
The operator generates values based on time and ignores downstream backpressure which - * may lead to {@code MissingBackpressureException} at some point in the chain. - * Consumers should consider applying one of the {@code onBackpressureXXX} operators as well.
+ * may lead to {@link MissingBackpressureException} at some point in the chain. + * Downstream consumers should consider applying one of the {@code onBackpressureXXX} operators as well. *
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param period @@ -2522,38 +2911,46 @@ public static Flowable interval(long period, TimeUnit unit) { * @param unit * time units to use for the interval size * @param scheduler - * the Scheduler to use for scheduling the items - * @return a Flowable that emits a sequential number each time interval + * the {@code Scheduler} to use for scheduling the items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Interval */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Flowable interval(long period, TimeUnit unit, Scheduler scheduler) { + @NonNull + public static Flowable interval(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return interval(period, period, unit, scheduler); } /** * Signals a range of long values, the first after some initial delay and the rest periodically after. *

- * The sequence completes immediately after the last value (start + count - 1) has been reached. + * The sequence completes immediately after the last value {@code (start + count - 1)} has been reached. *

*
Backpressure:
- *
The operator signals a {@code MissingBackpressureException} if the downstream can't keep up.
+ *
The operator signals a {@link MissingBackpressureException} if the downstream can't keep up.
*
Scheduler:
*
{@code intervalRange} by default operates on the {@link Schedulers#computation() computation} {@link Scheduler}.
*
* @param start that start value of the range - * @param count the number of values to emit in total, if zero, the operator emits an onComplete after the initial delay. + * @param count the number of values to emit in total, if zero, the operator emits an {@code onComplete} after the initial delay. * @param initialDelay the initial delay before signaling the first value (the start) * @param period the period between subsequent values - * @param unit the unit of measure of the initialDelay and period amounts - * @return the new Flowable instance + * @param unit the unit of measure of the {@code initialDelay} and {@code period} amounts + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds + * {@link Long#MAX_VALUE} + * @see #range(int, int) */ @CheckReturnValue + @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Flowable intervalRange(long start, long count, long initialDelay, long period, TimeUnit unit) { + public static Flowable intervalRange(long start, long count, long initialDelay, long period, @NonNull TimeUnit unit) { return intervalRange(start, count, initialDelay, period, unit, Schedulers.computation()); } @@ -2563,23 +2960,27 @@ public static Flowable intervalRange(long start, long count, long initialD * The sequence completes immediately after the last value (start + count - 1) has been reached. *
*
Backpressure:
- *
The operator signals a {@code MissingBackpressureException} if the downstream can't keep up.
+ *
The operator signals a {@link MissingBackpressureException} if the downstream can't keep up.
*
Scheduler:
*
you provide the {@link Scheduler}.
*
* @param start that start value of the range - * @param count the number of values to emit in total, if zero, the operator emits an onComplete after the initial delay. + * @param count the number of values to emit in total, if zero, the operator emits an {@code onComplete} after the initial delay. * @param initialDelay the initial delay before signaling the first value (the start) * @param period the period between subsequent values - * @param unit the unit of measure of the initialDelay and period amounts - * @param scheduler the target scheduler where the values and terminal signals will be emitted - * @return the new Flowable instance + * @param unit the unit of measure of the {@code initialDelay} and {@code period} amounts + * @param scheduler the target {@code Scheduler} where the values and terminal signals will be emitted + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds + * {@link Long#MAX_VALUE} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Flowable intervalRange(long start, long count, long initialDelay, long period, TimeUnit unit, Scheduler scheduler) { + public static Flowable intervalRange(long start, long count, long initialDelay, long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { if (count < 0L) { throw new IllegalArgumentException("count >= 0 required but it was " + count); } @@ -2591,19 +2992,19 @@ public static Flowable intervalRange(long start, long count, long initialD if (start > 0 && end < 0) { throw new IllegalArgumentException("Overflow! start + count is bigger than Long.MAX_VALUE"); } - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new FlowableIntervalRange(start, end, Math.max(0L, initialDelay), Math.max(0L, period), unit, scheduler)); } /** - * Returns a Flowable that signals the given (constant reference) item and then completes. + * Returns a {@code Flowable} that signals the given (constant reference) item and then completes. *

- * + * *

* Note that the item is taken and re-emitted as is and not computed by any means by {@code just}. Use {@link #fromCallable(Callable)} - * to generate a single item on demand (when {@code Subscriber}s subscribe to it). + * to generate a single item on demand (when {@link Subscriber}s subscribe to it). *

* See the multi-parameter overloads of {@code just} to emit more than one (constant reference) items one after the other. * Use {@link #fromArray(Object...)} to emit an arbitrary number of items that are known upfront. @@ -2620,7 +3021,8 @@ public static Flowable intervalRange(long start, long count, long initialD * the item to emit * @param * the type of that item - * @return a Flowable that emits {@code value} as a single item and then completes + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Just * @see #just(Object, Object) * @see #fromCallable(Callable) @@ -2631,15 +3033,15 @@ public static Flowable intervalRange(long start, long count, long initialD @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item) { - ObjectHelper.requireNonNull(item, "item is null"); - return RxJavaPlugins.onAssembly(new FlowableJust(item)); + public static <@NonNull T> Flowable just(T item) { + Objects.requireNonNull(item, "item is null"); + return RxJavaPlugins.onAssembly(new FlowableJust<>(item)); } /** - * Converts two items into a Publisher that emits those items. + * Converts two items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2653,25 +3055,25 @@ public static Flowable just(T item) { * second item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1} or {@code item2} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); + public static <@NonNull T> Flowable just(T item1, T item2) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); return fromArray(item1, item2); } /** - * Converts three items into a Publisher that emits those items. + * Converts three items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2687,26 +3089,26 @@ public static Flowable just(T item1, T item2) { * third item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2} or {@code item3} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); return fromArray(item1, item2, item3); } /** - * Converts four items into a Publisher that emits those items. + * Converts four items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2724,27 +3126,28 @@ public static Flowable just(T item1, T item2, T item3) { * fourth item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * or {@code item4} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); return fromArray(item1, item2, item3, item4); } /** - * Converts five items into a Publisher that emits those items. + * Converts five items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2764,28 +3167,29 @@ public static Flowable just(T item1, T item2, T item3, T item4) { * fifth item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4} or {@code item5} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4, T item5) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4, T item5) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); return fromArray(item1, item2, item3, item4, item5); } /** - * Converts six items into a Publisher that emits those items. + * Converts six items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2807,29 +3211,30 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5) * sixth item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5} or {@code item6} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4, T item5, T item6) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4, T item5, T item6) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); return fromArray(item1, item2, item3, item4, item5, item6); } /** - * Converts seven items into a Publisher that emits those items. + * Converts seven items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2853,30 +3258,32 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5, * seventh item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6} + * or {@code item7} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7); } /** - * Converts eight items into a Publisher that emits those items. + * Converts eight items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2902,31 +3309,33 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5, * eighth item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6}, + * {@code item7} or {@code item8} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); - ObjectHelper.requireNonNull(item8, "item8 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); + Objects.requireNonNull(item8, "item8 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7, item8); } /** - * Converts nine items into a Publisher that emits those items. + * Converts nine items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -2954,32 +3363,34 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5, * ninth item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6}, + * {@code item7}, {@code item8} or {@code item9} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8, T item9) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); - ObjectHelper.requireNonNull(item8, "item8 is null"); - ObjectHelper.requireNonNull(item9, "item9 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8, T item9) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); + Objects.requireNonNull(item8, "item8 is null"); + Objects.requireNonNull(item9, "item9 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7, item8, item9); } /** - * Converts ten items into a Publisher that emits those items. + * Converts ten items into a {@link Publisher} that emits those items. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals each value on-demand (i.e., when requested).
@@ -3009,51 +3420,54 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5, * tenth item * @param * the type of these items - * @return a Flowable that emits each item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6}, + * {@code item7}, {@code item8}, {@code item9}, + * or {@code item10} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8, T item9, T item10) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); - ObjectHelper.requireNonNull(item8, "item8 is null"); - ObjectHelper.requireNonNull(item9, "item9 is null"); - ObjectHelper.requireNonNull(item10, "item10 is null"); + public static <@NonNull T> Flowable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8, T item9, T item10) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); + Objects.requireNonNull(item8, "item8 is null"); + Objects.requireNonNull(item9, "item9 is null"); + Objects.requireNonNull(item10, "item10 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7, item8, item9, item10); } /** - * Flattens an Iterable of Publishers into one Publisher, without any transformation, while limiting the - * number of concurrent subscriptions to these Publishers. + * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, without any transformation, while limiting the + * number of concurrent subscriptions to these {@code Publisher}s. *

- * + * *

- * You can combine the items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable, int, int)} to merge sources and terminate only when all source {@code Publisher}s @@ -3063,15 +3477,15 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5, * * @param the common element base type * @param sources - * the Iterable of Publishers + * the {@code Iterable} of {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner Publisher - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the number of items to prefetch from each inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException - * if {@code maxConcurrency} is less than or equal to 0 + * if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Iterable, int, int) */ @@ -3079,32 +3493,33 @@ public static Flowable just(T item1, T item2, T item3, T item4, T item5, @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Iterable> sources, int maxConcurrency, int bufferSize) { + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Iterable<@NonNull ? extends Publisher> sources, int maxConcurrency, int bufferSize) { return fromIterable(sources).flatMap((Function)Functions.identity(), false, maxConcurrency, bufferSize); } /** - * Flattens an Iterable of Publishers into one Publisher, without any transformation, while limiting the - * number of concurrent subscriptions to these Publishers. + * Flattens an array of {@link Publisher}s into one {@code Publisher}, without any transformation, while limiting the + * number of concurrent subscriptions to these {@code Publisher}s. *

- * + * *

- * You can combine the items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeArrayDelayError(int, int, Publisher[])} to merge sources and terminate only when all source {@code Publisher}s @@ -3114,15 +3529,15 @@ public static Flowable merge(Iterable> s * * @param the common element base type * @param sources - * the array of Publishers + * the array of {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner Publisher - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the number of items to prefetch from each inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException - * if {@code maxConcurrency} is less than or equal to 0 + * if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge * @see #mergeArrayDelayError(int, int, Publisher...) */ @@ -3130,31 +3545,33 @@ public static Flowable merge(Iterable> s @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeArray(int maxConcurrency, int bufferSize, Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable mergeArray(int maxConcurrency, int bufferSize, @NonNull Publisher... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), false, maxConcurrency, bufferSize); } /** - * Flattens an Iterable of Publishers into one Publisher, without any transformation. + * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, without any transformation. *

- * + * *

- * You can combine the items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable)} to merge sources and terminate only when all source {@code Publisher}s @@ -3164,9 +3581,9 @@ public static Flowable mergeArray(int maxConcurrency, int bufferSize, Pub * * @param the common element base type * @param sources - * the Iterable of Publishers - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the {@code Iterable} of {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Iterable) */ @@ -3174,32 +3591,33 @@ public static Flowable mergeArray(int maxConcurrency, int bufferSize, Pub @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Iterable> sources) { + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Iterable<@NonNull ? extends Publisher> sources) { return fromIterable(sources).flatMap((Function)Functions.identity()); } /** - * Flattens an Iterable of Publishers into one Publisher, without any transformation, while limiting the - * number of concurrent subscriptions to these Publishers. + * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, without any transformation, while limiting the + * number of concurrent subscriptions to these {@code Publisher}s. *

- * + * *

- * You can combine the items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable, int)} to merge sources and terminate only when all source {@code Publisher}s @@ -3209,11 +3627,11 @@ public static Flowable merge(Iterable> s * * @param the common element base type * @param sources - * the Iterable of Publishers + * the {@code Iterable} of {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException * if {@code maxConcurrency} is less than or equal to 0 * @see ReactiveX operators documentation: Merge @@ -3223,33 +3641,34 @@ public static Flowable merge(Iterable> s @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Iterable> sources, int maxConcurrency) { + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Iterable<@NonNull ? extends Publisher> sources, int maxConcurrency) { return fromIterable(sources).flatMap((Function)Functions.identity(), maxConcurrency); } /** - * Flattens a Publisher that emits Publishers into a single Publisher that emits the items emitted by - * those Publishers, without any transformation. + * Flattens a {@link Publisher} that emits {@code Publisher}s into a single {@code Publisher} that emits the items emitted by + * thos {@code Publisher}s , without any transformation. *

- * + * *

- * You can combine the items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed * in unbounded mode (i.e., no backpressure is applied to it). The inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher)} to merge sources and terminate only when all source {@code Publisher}s @@ -3259,42 +3678,43 @@ public static Flowable merge(Iterable> s * * @param the common element base type * @param sources - * a Publisher that emits Publishers - * @return a Flowable that emits items that are the result of flattening the Publishers emitted by the - * {@code source} Publisher + * a {@code Publisher} that emits {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Publisher) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return merge(sources, bufferSize()); } /** - * Flattens a Publisher that emits Publishers into a single Publisher that emits the items emitted by - * those Publishers, without any transformation, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Flattens a {@link Publisher} that emits {@code Publisher}s into a single {@code Publisher} that emits the items emitted by + * those {@code Publisher}s, without any transformation, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. *

- * + * *

- * You can combine the items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine the items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher, int)} to merge sources and terminate only when all source {@code Publisher}s @@ -3304,11 +3724,11 @@ public static Flowable merge(Publisher> * * @param the common element base type * @param sources - * a Publisher that emits Publishers + * a {@code Publisher} that emits {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits items that are the result of flattening the Publishers emitted by the - * {@code source} Publisher + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException * if {@code maxConcurrency} is less than or equal to 0 * @see ReactiveX operators documentation: Merge @@ -3319,31 +3739,32 @@ public static Flowable merge(Publisher> @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Publisher> sources, int maxConcurrency) { + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Publisher<@NonNull ? extends Publisher> sources, int maxConcurrency) { return fromPublisher(sources).flatMap((Function)Functions.identity(), maxConcurrency); } /** - * Flattens an Array of Publishers into one Publisher, without any transformation. + * Flattens an array of {@link Publisher}s into one {@code Publisher}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeArrayDelayError(Publisher...)} to merge sources and terminate only when all source {@code Publisher}s @@ -3353,8 +3774,9 @@ public static Flowable merge(Publisher> * * @param the common element base type * @param sources - * the array of Publishers - * @return a Flowable that emits all of the items emitted by the Publishers in the Array + * the array of {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeArrayDelayError(Publisher...) */ @@ -3362,31 +3784,33 @@ public static Flowable merge(Publisher> @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeArray(Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable mergeArray(@NonNull Publisher... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), sources.length); } /** - * Flattens two Publishers into a single Publisher, without any transformation. + * Flattens two {@link Publisher}s into a single {@code Publisher}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher, Publisher)} to merge sources and terminate only when all source {@code Publisher}s @@ -3396,10 +3820,11 @@ public static Flowable mergeArray(Publisher... sources) { * * @param the common element base type * @param source1 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source2 - * a Publisher to be merged - * @return a Flowable that emits all of the items emitted by the source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Publisher, Publisher) */ @@ -3408,33 +3833,33 @@ public static Flowable mergeArray(Publisher... sources) { @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Publisher source1, Publisher source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T> Flowable merge(@NonNull Publisher source1, @NonNull Publisher source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return fromArray(source1, source2).flatMap((Function)Functions.identity(), false, 2); } /** - * Flattens three Publishers into a single Publisher, without any transformation. + * Flattens three {@link Publisher}s into a single {@code Publisher}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher, Publisher, Publisher)} to merge sources and terminate only when all source {@code Publisher}s @@ -3444,12 +3869,13 @@ public static Flowable merge(Publisher source1, Publisher the common element base type * @param source1 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source2 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source3 - * a Publisher to be merged - * @return a Flowable that emits all of the items emitted by the source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Publisher, Publisher, Publisher) */ @@ -3458,34 +3884,34 @@ public static Flowable merge(Publisher source1, Publisher Flowable merge(Publisher source1, Publisher source2, Publisher source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T> Flowable merge(@NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return fromArray(source1, source2, source3).flatMap((Function)Functions.identity(), false, 3); } /** - * Flattens four Publishers into a single Publisher, without any transformation. + * Flattens four {@link Publisher}s into a single {@code Publisher}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code merge} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code Publisher}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code Publisher}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code Publisher}s are canceled. * If more than one {@code Publisher} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been canceled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher, Publisher, Publisher, Publisher)} to merge sources and terminate only when all source {@code Publisher}s @@ -3495,14 +3921,15 @@ public static Flowable merge(Publisher source1, Publisher the common element base type * @param source1 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source2 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source3 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source4 - * a Publisher to be merged - * @return a Flowable that emits all of the items emitted by the source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Publisher, Publisher, Publisher, Publisher) */ @@ -3511,235 +3938,245 @@ public static Flowable merge(Publisher source1, Publisher Flowable merge( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T> Flowable merge( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return fromArray(source1, source2, source3, source4).flatMap((Function)Functions.identity(), false, 4); } /** - * Flattens an Iterable of Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source Publishers without being interrupted by an error + * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from each of the source {@code Publisher}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. All inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}.
*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * the Iterable of Publishers - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the {@code Iterable} of {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Iterable> sources) { + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources) { return fromIterable(sources).flatMap((Function)Functions.identity(), true); } /** - * Flattens an Iterable of Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source Publishers without being interrupted by an error - * notification from one of them, while limiting the number of concurrent subscriptions to these Publishers. + * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from each of the source {@code Publisher}s without being interrupted by an error + * notification from one of them, while limiting the number of concurrent subscriptions to these {@code Publisher}s. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. All inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * the Iterable of Publishers + * the {@code Iterable} of {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner Publisher - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the number of items to prefetch from each inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Iterable> sources, int maxConcurrency, int bufferSize) { + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources, int maxConcurrency, int bufferSize) { return fromIterable(sources).flatMap((Function)Functions.identity(), true, maxConcurrency, bufferSize); } /** - * Flattens an array of Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source Publishers without being interrupted by an error - * notification from one of them, while limiting the number of concurrent subscriptions to these Publishers. + * Flattens an array of {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from each of the source {@code Publisher}s without being interrupted by an error + * notification from one of them, while limiting the number of concurrent subscriptions to these {@code Publisher}s. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. All source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * the array of Publishers + * the array of {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner Publisher - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the number of items to prefetch from each inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeArrayDelayError(int maxConcurrency, int bufferSize, Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable mergeArrayDelayError(int maxConcurrency, int bufferSize, @NonNull Publisher... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), true, maxConcurrency, bufferSize); } /** - * Flattens an Iterable of Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source Publishers without being interrupted by an error - * notification from one of them, while limiting the number of concurrent subscriptions to these Publishers. + * Flattens an {@link Iterable} of {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from each of the source {@code Publisher}s without being interrupted by an error + * notification from one of them, while limiting the number of concurrent subscriptions to these {@code Publisher}s. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. All inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * the Iterable of Publishers + * the {@code Iterable} of {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Iterable> sources, int maxConcurrency) { + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Iterable<@NonNull ? extends Publisher> sources, int maxConcurrency) { return fromIterable(sources).flatMap((Function)Functions.identity(), true, maxConcurrency); } /** - * Flattens a Publisher that emits Publishers into one Publisher, in a way that allows a Subscriber to - * receive all successfully emitted items from all of the source Publishers without being interrupted by + * Flattens a {@link Publisher} that emits {@code Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to + * receive all successfully emitted items from all of the source {@code Publisher}s without being interrupted by * an error notification from one of them. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed * in unbounded mode (i.e., no backpressure is applied to it). The inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * a Publisher that emits Publishers - * @return a Flowable that emits all of the items emitted by the Publishers emitted by the - * {@code source} Publisher + * a {@code Publisher} that emits {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return mergeDelayError(sources, bufferSize()); } /** - * Flattens a Publisher that emits Publishers into one Publisher, in a way that allows a Subscriber to - * receive all successfully emitted items from all of the source Publishers without being interrupted by + * Flattens a {@link Publisher} that emits {@code Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to + * receive all successfully emitted items from all of the source {@code Publisher}s without being interrupted by * an error notification from one of them, while limiting the - * number of concurrent subscriptions to these Publishers. + * number of concurrent subscriptions to these {@code Publisher}s. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * a Publisher that emits Publishers + * a {@code Publisher} that emits {@code Publisher}s * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits all of the items emitted by the Publishers emitted by the - * {@code source} Publisher + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: Merge * @since 2.0 */ @@ -3747,73 +4184,77 @@ public static Flowable mergeDelayError(Publisher Flowable mergeDelayError(Publisher> sources, int maxConcurrency) { + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources, int maxConcurrency) { return fromPublisher(sources).flatMap((Function)Functions.identity(), true, maxConcurrency); } /** - * Flattens an array of Publishers into one Flowable, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source Publishers without being interrupted by an error + * Flattens an array of {@link Publisher}s into one {@code Flowable}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from each of the source {@code Publisher}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged Publishers notify of an + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code Publisher}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged Publishers have finished emitting items. + * error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. Both the outer and inner {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param sources - * the Iterable of Publishers - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * Publishers in the Iterable + * the array of {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeArrayDelayError(Publisher... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable mergeArrayDelayError(@NonNull Publisher... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), true, sources.length); } /** - * Flattens two Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source Publishers without being interrupted by an error + * Flattens two {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from each of the source {@code Publisher}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(Publisher, Publisher)} except that if any of the merged Publishers + * This behaves like {@link #merge(Publisher, Publisher)} except that if any of the merged {@code Publisher}s * notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from - * propagating that error notification until all of the merged Publishers have finished emitting items. + * propagating that error notification until all of the merged {@code Publisher}s have finished emitting items. *

- * + * *

- * Even if both merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if both merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param source1 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source2 - * a Publisher to be merged - * @return a Flowable that emits all of the items that are emitted by the two source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -3821,42 +4262,43 @@ public static Flowable mergeArrayDelayError(Publisher... sou @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Publisher source1, Publisher source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher source1, @NonNull Publisher source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return fromArray(source1, source2).flatMap((Function)Functions.identity(), true, 2); } /** - * Flattens three Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from all of the source Publishers without being interrupted by an error + * Flattens three {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from all of the source {@code Publisher}s without being interrupted by an error * notification from one of them. *

* This behaves like {@link #merge(Publisher, Publisher, Publisher)} except that if any of the merged - * Publishers notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged Publishers have finished emitting + * {@code Publisher}s notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged {@code Publisher}s have finished emitting * items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param source1 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source2 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source3 - * a Publisher to be merged - * @return a Flowable that emits all of the items that are emitted by the source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -3864,45 +4306,46 @@ public static Flowable mergeDelayError(Publisher source1, Pu @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Publisher source1, Publisher source2, Publisher source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return fromArray(source1, source2, source3).flatMap((Function)Functions.identity(), true, 3); } /** - * Flattens four Publishers into one Publisher, in a way that allows a Subscriber to receive all - * successfully emitted items from all of the source Publishers without being interrupted by an error + * Flattens four {@link Publisher}s into one {@code Publisher}, in a way that allows a {@link Subscriber} to receive all + * successfully emitted items from all of the source {@code Publisher}s without being interrupted by an error * notification from one of them. *

* This behaves like {@link #merge(Publisher, Publisher, Publisher, Publisher)} except that if any of - * the merged Publishers notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} - * will refrain from propagating that error notification until all of the merged Publishers have finished + * the merged {@code Publisher}s notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} + * will refrain from propagating that error notification until all of the merged {@code Publisher}s have finished * emitting items. *

- * + * *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code Publisher}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its {@code Subscriber}s once. *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type * @param source1 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source2 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source3 - * a Publisher to be merged + * a {@code Publisher} to be merged * @param source4 - * a Publisher to be merged - * @return a Flowable that emits all of the items that are emitted by the source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -3910,22 +4353,22 @@ public static Flowable mergeDelayError(Publisher source1, Pu @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T> Flowable mergeDelayError( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return fromArray(source1, source2, source3, source4).flatMap((Function)Functions.identity(), true, 4); } /** - * Returns a Flowable that never sends any items or notifications to a {@link Subscriber}. + * Returns a {@code Flowable} that never sends any items or notifications to a {@link Subscriber}. *

- * + * *

- * This Publisher is useful primarily for testing purposes. + * This {@link Publisher} is useful primarily for testing purposes. *

*
Backpressure:
*
This source doesn't produce any elements and effectively ignores downstream backpressure.
@@ -3934,22 +4377,23 @@ public static Flowable mergeDelayError( *
* * @param - * the type of items (not) emitted by the Publisher - * @return a Flowable that never emits any items or sends any notifications to a {@link Subscriber} + * the type of items (not) emitted by the {@code Publisher} + * @return the shared {@code Flowable} instance * @see ReactiveX operators documentation: Never */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Flowable never() { + @NonNull + public static <@NonNull T> Flowable never() { return RxJavaPlugins.onAssembly((Flowable) FlowableNever.INSTANCE); } /** - * Returns a Flowable that emits a sequence of Integers within a specified range. + * Returns a {@code Flowable} that emits a sequence of {@link Integer}s within a specified range. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals values on-demand (i.e., when requested).
@@ -3958,18 +4402,21 @@ public static Flowable never() { *
* * @param start - * the value of the first Integer in the sequence + * the value of the first {@code Integer} in the sequence * @param count - * the number of sequential Integers to generate - * @return a Flowable that emits a range of sequential Integers + * the number of sequential {@code Integer}s to generate + * @return the new {@code Flowable} instance * @throws IllegalArgumentException * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds - * {@code Integer.MAX_VALUE} + * {@link Integer#MAX_VALUE} * @see ReactiveX operators documentation: Range + * @see #rangeLong(long, long) + * @see #intervalRange(long, long, long, long, TimeUnit) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public static Flowable range(int start, int count) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); @@ -3987,9 +4434,9 @@ public static Flowable range(int start, int count) { } /** - * Returns a Flowable that emits a sequence of Longs within a specified range. + * Returns a {@code Flowable} that emits a sequence of {@link Long}s within a specified range. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream and signals values on-demand (i.e., when requested).
@@ -3998,18 +4445,19 @@ public static Flowable range(int start, int count) { *
* * @param start - * the value of the first Long in the sequence + * the value of the first {@code Long} in the sequence * @param count - * the number of sequential Longs to generate - * @return a Flowable that emits a range of sequential Longs + * the number of sequential {@code Long}s to generate + * @return the new {@code Flowable} instance * @throws IllegalArgumentException * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds - * {@code Long.MAX_VALUE} + * {@link Long#MAX_VALUE} * @see ReactiveX operators documentation: Range */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public static Flowable rangeLong(long start, long count) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); @@ -4032,302 +4480,315 @@ public static Flowable rangeLong(long start, long count) { } /** - * Returns a Single that emits a Boolean value that indicates whether two Publisher sequences are the - * same by comparing the items emitted by each Publisher pairwise. + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link Publisher} sequences are the + * same by comparing the items emitted by each {@code Publisher} pairwise. *

- * + * *

*
Backpressure:
*
This operator honors downstream backpressure and expects both of its sources - * to honor backpressure as well. If violated, the operator will emit a MissingBackpressureException.
+ * to honor backpressure as well. If violated, the operator will emit a {@link MissingBackpressureException}. *
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first Publisher to compare + * the first {@code Publisher} to compare * @param source2 - * the second Publisher to compare + * the second {@code Publisher} to compare * @param - * the type of items emitted by each Publisher - * @return a Flowable that emits a Boolean value that indicates whether the two sequences are the same + * the type of items emitted by each {@code Publisher} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(Publisher source1, Publisher source2) { + @NonNull + public static <@NonNull T> Single sequenceEqual(@NonNull Publisher source1, @NonNull Publisher source2) { return sequenceEqual(source1, source2, ObjectHelper.equalsPredicate(), bufferSize()); } /** - * Returns a Single that emits a Boolean value that indicates whether two Publisher sequences are the - * same by comparing the items emitted by each Publisher pairwise based on the results of a specified + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link Publisher} sequences are the + * same by comparing the items emitted by each {@code Publisher} pairwise based on the results of a specified * equality function. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator signals a {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator signals a {@link MissingBackpressureException}. *
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first Publisher to compare + * the first {@code Publisher} to compare * @param source2 - * the second Publisher to compare + * the second {@code Publisher} to compare * @param isEqual - * a function used to compare items emitted by each Publisher + * a function used to compare items emitted by each {@code Publisher} * @param - * the type of items emitted by each Publisher - * @return a Single that emits a Boolean value that indicates whether the two Publisher sequences - * are the same according to the specified function + * the type of items emitted by each {@code Publisher} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code isEqual} is {@code null} * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(Publisher source1, Publisher source2, - BiPredicate isEqual) { + @NonNull + public static <@NonNull T> Single sequenceEqual(@NonNull Publisher source1, @NonNull Publisher source2, + @NonNull BiPredicate isEqual) { return sequenceEqual(source1, source2, isEqual, bufferSize()); } /** - * Returns a Single that emits a Boolean value that indicates whether two Publisher sequences are the - * same by comparing the items emitted by each Publisher pairwise based on the results of a specified + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link Publisher} sequences are the + * same by comparing the items emitted by each {@code Publisher} pairwise based on the results of a specified * equality function. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The source {@code Publisher}s are expected to honor - * backpressure; if violated, the operator signals a {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator signals a {@link MissingBackpressureException}. *
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first Publisher to compare + * the first {@code Publisher} to compare * @param source2 - * the second Publisher to compare + * the second {@code Publisher} to compare * @param isEqual - * a function used to compare items emitted by each Publisher + * a function used to compare items emitted by each {@code Publisher} * @param bufferSize - * the number of items to prefetch from the first and second source Publisher + * the number of items to prefetch from the first and second source {@code Publisher} * @param - * the type of items emitted by each Publisher - * @return a Single that emits a Boolean value that indicates whether the two Publisher sequences - * are the same according to the specified function + * the type of items emitted by each {@code Publisher} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code isEqual} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(Publisher source1, Publisher source2, - BiPredicate isEqual, int bufferSize) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(isEqual, "isEqual is null"); + public static <@NonNull T> Single sequenceEqual(@NonNull Publisher source1, @NonNull Publisher source2, + @NonNull BiPredicate isEqual, int bufferSize) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(isEqual, "isEqual is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableSequenceEqualSingle(source1, source2, isEqual, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableSequenceEqualSingle<>(source1, source2, isEqual, bufferSize)); } /** - * Returns a Single that emits a Boolean value that indicates whether two Publisher sequences are the - * same by comparing the items emitted by each Publisher pairwise. + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link Publisher} sequences are the + * same by comparing the items emitted by each {@code Publisher} pairwise. *

- * + * *

*
Backpressure:
*
This operator honors downstream backpressure and expects both of its sources - * to honor backpressure as well. If violated, the operator will emit a MissingBackpressureException.
+ * to honor backpressure as well. If violated, the operator will emit a {@link MissingBackpressureException}. *
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first Publisher to compare + * the first {@code Publisher} to compare * @param source2 - * the second Publisher to compare + * the second {@code Publisher} to compare * @param bufferSize - * the number of items to prefetch from the first and second source Publisher + * the number of items to prefetch from the first and second source {@code Publisher} * @param - * the type of items emitted by each Publisher - * @return a Single that emits a Boolean value that indicates whether the two sequences are the same + * the type of items emitted by each {@code Publisher} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(Publisher source1, Publisher source2, int bufferSize) { + @NonNull + public static <@NonNull T> Single sequenceEqual(@NonNull Publisher source1, @NonNull Publisher source2, int bufferSize) { return sequenceEqual(source1, source2, ObjectHelper.equalsPredicate(), bufferSize); } /** - * Converts a Publisher that emits Publishers into a Publisher that emits the items emitted by the - * most recently emitted of those Publishers. + * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the + * most recently emitted of those {@code Publisher}s. *

- * + * *

- * {@code switchOnNext} subscribes to a Publisher that emits Publishers. Each time it observes one of - * these emitted Publishers, the Publisher returned by {@code switchOnNext} begins emitting the items - * emitted by that Publisher. When a new Publisher is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted Publisher and begins emitting items from the new one. + * {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of + * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code Publisher}. When a new {@code Publisher} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code Publisher} and begins emitting items from the new one. *

- * The resulting Publisher completes if both the outer Publisher and the last inner Publisher, if any, complete. - * If the outer Publisher signals an onError, the inner Publisher is canceled and the error delivered in-sequence. + * The resulting {@code Flowable} completes if both the outer {@code Publisher} and the last inner {@code Publisher}, if any, complete. + * If the outer {@code Publisher} signals an {@code onError}, the inner {@code Publisher} is canceled and the error delivered in-sequence. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
*
* * @param the item type * @param sources - * the source Publisher that emits Publishers + * the source {@code Publisher} that emits {@code Publisher}s * @param bufferSize - * the number of items to prefetch from the inner Publishers - * @return a Flowable that emits the items emitted by the Publisher most recently emitted by the source - * Publisher + * the number of items to prefetch from the inner {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Switch */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable switchOnNext(Publisher> sources, int bufferSize) { + @NonNull + public static <@NonNull T> Flowable switchOnNext(@NonNull Publisher> sources, int bufferSize) { return fromPublisher(sources).switchMap((Function)Functions.identity(), bufferSize); } /** - * Converts a Publisher that emits Publishers into a Publisher that emits the items emitted by the - * most recently emitted of those Publishers. + * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the + * most recently emitted of those {@code Publisher}s. *

- * + * *

- * {@code switchOnNext} subscribes to a Publisher that emits Publishers. Each time it observes one of - * these emitted Publishers, the Publisher returned by {@code switchOnNext} begins emitting the items - * emitted by that Publisher. When a new Publisher is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted Publisher and begins emitting items from the new one. + * {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of + * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code Publisher}. When a new {@code Publisher} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code Publisher} and begins emitting items from the new one. *

- * The resulting Publisher completes if both the outer Publisher and the last inner Publisher, if any, complete. - * If the outer Publisher signals an onError, the inner Publisher is canceled and the error delivered in-sequence. + * The resulting {@code Flowable} completes if both the outer {@code Publisher} and the last inner {@code Publisher}, if any, complete. + * If the outer {@code Publisher} signals an {@code onError}, the inner {@code Publisher} is canceled and the error delivered in-sequence. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
*
* * @param the item type * @param sources - * the source Publisher that emits Publishers - * @return a Flowable that emits the items emitted by the Publisher most recently emitted by the source - * Publisher + * the source {@code Publisher} that emits {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Switch */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable switchOnNext(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable switchOnNext(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return fromPublisher(sources).switchMap((Function)Functions.identity()); } /** - * Converts a Publisher that emits Publishers into a Publisher that emits the items emitted by the - * most recently emitted of those Publishers and delays any exception until all Publishers terminate. + * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the + * most recently emitted of those {@code Publisher}s and delays any exception until all {@code Publisher}s terminate. *

- * + * *

- * {@code switchOnNext} subscribes to a Publisher that emits Publishers. Each time it observes one of - * these emitted Publishers, the Publisher returned by {@code switchOnNext} begins emitting the items - * emitted by that Publisher. When a new Publisher is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted Publisher and begins emitting items from the new one. + * {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of + * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code Publisher}. When a new {@code Publisher} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code Publisher} and begins emitting items from the new one. *

- * The resulting Publisher completes if both the main Publisher and the last inner Publisher, if any, complete. - * If the main Publisher signals an onError, the termination of the last inner Publisher will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner Publishers signaled. + * The resulting {@code Flowable} completes if both the main {@code Publisher} and the last inner {@code Publisher}, if any, complete. + * If the main {@code Publisher} signals an {@code onError}, the termination of the last inner {@code Publisher} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code Publisher}s signaled. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the item type * @param sources - * the source Publisher that emits Publishers - * @return a Flowable that emits the items emitted by the Publisher most recently emitted by the source - * Publisher + * the source {@code Publisher} that emits {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Switch * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable switchOnNextDelayError(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable switchOnNextDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources) { return switchOnNextDelayError(sources, bufferSize()); } /** - * Converts a Publisher that emits Publishers into a Publisher that emits the items emitted by the - * most recently emitted of those Publishers and delays any exception until all Publishers terminate. + * Converts a {@link Publisher} that emits {@code Publisher}s into a {@code Publisher} that emits the items emitted by the + * most recently emitted of those {@code Publisher}s and delays any exception until all {@code Publisher}s terminate. *

- * + * *

- * {@code switchOnNext} subscribes to a Publisher that emits Publishers. Each time it observes one of - * these emitted Publishers, the Publisher returned by {@code switchOnNext} begins emitting the items - * emitted by that Publisher. When a new Publisher is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted Publisher and begins emitting items from the new one. + * {@code switchOnNext} subscribes to a {@code Publisher} that emits {@code Publisher}s. Each time it observes one of + * these emitted {@code Publisher}s, the {@code Publisher} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code Publisher}. When a new {@code Publisher} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code Publisher} and begins emitting items from the new one. *

- * The resulting Publisher completes if both the main Publisher and the last inner Publisher, if any, complete. - * If the main Publisher signals an onError, the termination of the last inner Publisher will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner Publishers signaled. + * The resulting {@code Flowable} completes if both the main {@code Publisher} and the last inner {@code Publisher}, if any, complete. + * If the main {@code Publisher} signals an {@code onError}, the termination of the last inner {@code Publisher} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code Publisher}s signaled. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the item type * @param sources - * the source Publisher that emits Publishers + * the source {@code Publisher} that emits {@code Publisher}s * @param prefetch - * the number of items to prefetch from the inner Publishers - * @return a Flowable that emits the items emitted by the Publisher most recently emitted by the source - * Publisher + * the number of items to prefetch from the inner {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: Switch * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable switchOnNextDelayError(Publisher> sources, int prefetch) { + @NonNull + public static <@NonNull T> Flowable switchOnNextDelayError(@NonNull Publisher<@NonNull ? extends Publisher> sources, int prefetch) { return fromPublisher(sources).switchMapDelayError(Functions.>identity(), prefetch); } /** - * Returns a Flowable that emits {@code 0L} after a specified delay, and then completes. + * Returns a {@code Flowable} that emits {@code 0L} after a specified delay, and then completes. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. If the downstream needs a slower rate @@ -4340,27 +4801,29 @@ public static Flowable switchOnNextDelayError(PublisherReactiveX operators documentation: Timer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Flowable timer(long delay, TimeUnit unit) { + @NonNull + public static Flowable timer(long delay, @NonNull TimeUnit unit) { return timer(delay, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits {@code 0L} after a specified delay, on a specified Scheduler, and then + * Returns a {@code Flowable} that emits {@code 0L} after a specified delay, on a specified {@link Scheduler}, and then * completes. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time. If the downstream needs a slower rate * it should slow the timer or use something like {@link #onBackpressureDrop}.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param delay @@ -4368,36 +4831,37 @@ public static Flowable timer(long delay, TimeUnit unit) { * @param unit * time units to use for {@code delay} * @param scheduler - * the {@link Scheduler} to use for scheduling the item - * @return a Flowable that emits {@code 0L} after a specified delay, on a specified Scheduler, and then - * completes + * the {@code Scheduler} to use for scheduling the item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timer */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Flowable timer(long delay, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public static Flowable timer(long delay, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new FlowableTimer(Math.max(0L, delay), unit, scheduler)); } /** - * Create a Flowable by wrapping a Publisher which has to be implemented according - * to the Reactive Streams specification by handling backpressure and - * cancellation correctly; no safeguards are provided by the Flowable itself. + * Create a {@code Flowable} by wrapping a {@link Publisher} which has to be implemented according + * to the Reactive Streams specification by handling backpressure and + * cancellation correctly; no safeguards are provided by the {@code Flowable} itself. *
*
Backpressure:
*
This operator is a pass-through for backpressure and the behavior is determined by the - * provided Publisher implementation.
+ * provided {@code Publisher} implementation.
*
Scheduler:
*
{@code unsafeCreate} by default doesn't operate on any particular {@link Scheduler}.
*
* @param the value type emitted - * @param onSubscribe the Publisher instance to wrap - * @return the new Flowable instance + * @param onSubscribe the {@code Publisher} instance to wrap + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} * @throws IllegalArgumentException if {@code onSubscribe} is a subclass of {@code Flowable}; such * instances don't need conversion and is possibly a port remnant from 1.x or one should use {@link #hide()} * instead. @@ -4406,75 +4870,80 @@ public static Flowable timer(long delay, TimeUnit unit, Scheduler schedule @NonNull @BackpressureSupport(BackpressureKind.NONE) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable unsafeCreate(Publisher onSubscribe) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); + public static <@NonNull T> Flowable unsafeCreate(@NonNull Publisher onSubscribe) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); if (onSubscribe instanceof Flowable) { throw new IllegalArgumentException("unsafeCreate(Flowable) should be upgraded"); } - return RxJavaPlugins.onAssembly(new FlowableFromPublisher(onSubscribe)); + return RxJavaPlugins.onAssembly(new FlowableFromPublisher<>(onSubscribe)); } /** - * Constructs a Publisher that creates a dependent resource object which is disposed of on cancellation. + * Constructs a {@code Flowable} that creates a dependent resource object, a {@link Publisher} with + * that resource and calls the provided {@code resourceDisposer} function if this inner source terminates or the + * downstream cancels the flow. *

- * + * *

*
Backpressure:
*
The operator is a pass-through for backpressure and otherwise depends on the - * backpressure support of the Publisher returned by the {@code resourceFactory}.
+ * backpressure support of the {@code Publisher} returned by the {@code resourceFactory}. *
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the generated Publisher + * @param the element type of the generated {@code Publisher} * @param the type of the resource associated with the output sequence * @param resourceSupplier - * the factory function to create a resource object that depends on the Publisher + * the factory function to create a resource object that depends on the {@code Publisher} * @param sourceSupplier - * the factory function to create a Publisher - * @param resourceDisposer + * the factory function to create a {@code Publisher} + * @param resourceCleanup * the function that will dispose of the resource - * @return the Publisher whose lifetime controls the lifetime of the dependent resource object + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} or {@code resourceCleanup} is {@code null} * @see ReactiveX operators documentation: Using */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable using(Supplier resourceSupplier, - Function> sourceSupplier, Consumer resourceDisposer) { - return using(resourceSupplier, sourceSupplier, resourceDisposer, true); + @NonNull + public static <@NonNull T, @NonNull D> Flowable using( + @NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup) { + return using(resourceSupplier, sourceSupplier, resourceCleanup, true); } /** - * Constructs a Publisher that creates a dependent resource object which is disposed of just before - * termination if you have set {@code disposeEagerly} to {@code true} and cancellation does not occur - * before termination. Otherwise, resource disposal will occur on cancellation. Eager disposal is - * particularly appropriate for a synchronous Publisher that reuses resources. {@code disposeAction} will - * only be called once per subscription. + * Constructs a {@code Flowable} that creates a dependent resource object, a {@link Publisher} with + * that resource and calls the provided {@code resourceDisposer} function if this inner source terminates or the + * downstream disposes the flow; doing it before these end-states have been reached if {@code eager == true}, after otherwise. *

- * + * *

*
Backpressure:
*
The operator is a pass-through for backpressure and otherwise depends on the - * backpressure support of the Publisher returned by the {@code resourceFactory}.
+ * backpressure support of the {@code Publisher} returned by the {@code resourceFactory}. *
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the generated Publisher + * @param the element type of the generated {@code Publisher} * @param the type of the resource associated with the output sequence * @param resourceSupplier - * the factory function to create a resource object that depends on the Publisher + * the factory function to create a resource object that depends on the {@code Publisher} * @param sourceSupplier - * the factory function to create a Publisher - * @param resourceDisposer + * the factory function to create a {@code Publisher} + * @param resourceCleanup * the function that will dispose of the resource * @param eager - * If {@code true} then resource disposal will happen either on a {@code cancel()} call before the upstream is disposed + * If {@code true}, the resource disposal will happen either on a {@code cancel()} call before the upstream is disposed * or just before the emission of a terminal event ({@code onComplete} or {@code onError}). * If {@code false} the resource disposal will happen either on a {@code cancel()} call after the upstream is disposed * or just after the emission of a terminal event ({@code onComplete} or {@code onError}). - * @return the Publisher whose lifetime controls the lifetime of the dependent resource object + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} or {@code resourceCleanup} is {@code null} * @see ReactiveX operators documentation: Using * @since 2.0 */ @@ -4482,26 +4951,28 @@ public static Flowable using(Supplier resourceSupplier, @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable using(Supplier resourceSupplier, - Function> sourceSupplier, - Consumer resourceDisposer, boolean eager) { - ObjectHelper.requireNonNull(resourceSupplier, "resourceSupplier is null"); - ObjectHelper.requireNonNull(sourceSupplier, "sourceSupplier is null"); - ObjectHelper.requireNonNull(resourceDisposer, "resourceDisposer is null"); - return RxJavaPlugins.onAssembly(new FlowableUsing(resourceSupplier, sourceSupplier, resourceDisposer, eager)); + public static <@NonNull T, @NonNull D> Flowable using( + @NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup, + boolean eager) { + Objects.requireNonNull(resourceSupplier, "resourceSupplier is null"); + Objects.requireNonNull(sourceSupplier, "sourceSupplier is null"); + Objects.requireNonNull(resourceCleanup, "resourceCleanup is null"); + return RxJavaPlugins.onAssembly(new FlowableUsing(resourceSupplier, sourceSupplier, resourceCleanup, eager)); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an Iterable of other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an {@link Iterable} of other {@link Publisher}s. *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@code onNext} as many times as - * the number of {@code onNext} invocations of the source Publisher that emits the fewest items. + * The resulting {@code Flowable} returned from {@code zip} will invoke {@code onNext} as many times as + * the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if * one of the sources is shorter than the rest while canceling the other sources. Therefore, it @@ -4515,11 +4986,11 @@ public static Flowable using(Supplier resourceSupplier, * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4528,34 +4999,35 @@ public static Flowable using(Supplier resourceSupplier, * @param the common value type * @param the zipped result type * @param sources - * an Iterable of source Publishers + * an {@code Iterable} of source {@code Publisher}s * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip(Iterable> sources, Function zipper) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new FlowableZip(null, sources, zipper, bufferSize(), false)); + public static <@NonNull T, @NonNull R> Flowable zip(@NonNull Iterable<@NonNull ? extends Publisher> sources, @NonNull Function zipper) { + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableZip<>(null, sources, zipper, bufferSize(), false)); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an Iterable of other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an {@link Iterable} of other {@link Publisher}s. *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@code onNext} as many times as - * the number of {@code onNext} invocations of the source Publisher that emits the fewest items. + * The resulting {@code Floawble} returned from {@code zip} will invoke {@code onNext} as many times as + * the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if * one of the sources is shorter than the rest while canceling the other sources. Therefore, it @@ -4569,11 +5041,11 @@ public static Flowable zip(Iterable> * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4581,45 +5053,47 @@ public static Flowable zip(Iterable> * * * @param sources - * an Iterable of source Publishers + * an {@code Iterable} of source {@code Publisher}s * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} * @param delayError - * delay errors signaled by any of the source Publisher until all Publishers terminate + * delay errors signaled by any of the source {@code Publisher} until all {@code Publisher}s terminate * @param bufferSize - * the number of elements to prefetch from each source Publisher + * the number of elements to prefetch from each source {@code Publisher} * @param the common source value type * @param the zipped result type - * @return a Flowable that emits the zipped results + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip(Iterable> sources, - Function zipper, boolean delayError, + public static <@NonNull T, @NonNull R> Flowable zip(@NonNull Iterable<@NonNull ? extends Publisher> sources, + @NonNull Function zipper, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableZip(null, sources, zipper, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new FlowableZip<>(null, sources, zipper, bufferSize, delayError)); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -4636,7 +5110,7 @@ public static Flowable zip(Iterable> *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4646,41 +5120,42 @@ public static Flowable zip(Iterable> * @param the value type of the second source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results - * in an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results + * in an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, - BiFunction zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull BiFunction zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -4697,7 +5172,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4707,42 +5182,43 @@ public static Flowable zip( * @param the value type of the second source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results - * in an item that will be emitted by the resulting Publisher - * @param delayError delay errors from any of the source Publishers till the other terminates - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results + * in an item that will be emitted by the resulting {@code Flowable} + * @param delayError delay errors from any of the source {@code Publisher}s till the other terminates + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, - BiFunction zipper, boolean delayError) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull BiFunction zipper, boolean delayError) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), delayError, bufferSize(), source1, source2); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -4759,7 +5235,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4769,44 +5245,46 @@ public static Flowable zip( * @param the value type of the second source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results - * in an item that will be emitted by the resulting Publisher - * @param delayError delay errors from any of the source Publishers till the other terminates - * @param bufferSize the number of elements to prefetch from each source Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results + * in an item that will be emitted by the resulting {@code Flowable} + * @param delayError delay errors from any of the source {@code Publisher}s till the other terminates + * @param bufferSize the number of elements to prefetch from each source {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, - BiFunction zipper, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull BiFunction zipper, boolean delayError, int bufferSize) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), delayError, bufferSize, source1, source2); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * three items emitted, in sequence, by three other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * three items emitted, in sequence, by three other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -4823,7 +5301,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4834,45 +5312,46 @@ public static Flowable zip( * @param the value type of the third source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Function3 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Function3 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * four items emitted, in sequence, by four other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * four items emitted, in sequence, by four other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -4889,7 +5368,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4901,49 +5380,51 @@ public static Flowable zip( * @param the value type of the fourth source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param source4 - * a fourth source Publisher + * a fourth source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Publisher source4, - Function4 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Publisher source4, + @NonNull Function4 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * five items emitted, in sequence, by five other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * five items emitted, in sequence, by five other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -4960,7 +5441,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4973,51 +5454,53 @@ public static Flowable zip( * @param the value type of the fifth source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param source4 - * a fourth source Publisher + * a fourth source {@code Publisher} * @param source5 - * a fifth source Publisher + * a fifth source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Publisher source4, Publisher source5, - Function5 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Publisher source4, @NonNull Publisher source5, + @NonNull Function5 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * six items emitted, in sequence, by six other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * six items emitted, in sequence, by six other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -5034,7 +5517,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -5048,54 +5531,57 @@ public static Flowable zip( * @param the value type of the sixth source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param source4 - * a fourth source Publisher + * a fourth source {@code Publisher} * @param source5 - * a fifth source Publisher + * a fifth source {@code Publisher} * @param source6 - * a sixth source Publisher + * a sixth source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6} + * or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Publisher source4, Publisher source5, Publisher source6, - Function6 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Publisher source4, @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Function6 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * seven items emitted, in sequence, by seven other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * seven items emitted, in sequence, by seven other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -5112,7 +5598,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -5127,58 +5613,61 @@ public static Flowable zip( * @param the value type of the seventh source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param source4 - * a fourth source Publisher + * a fourth source {@code Publisher} * @param source5 - * a fifth source Publisher + * a fifth source {@code Publisher} * @param source6 - * a sixth source Publisher + * a sixth source {@code Publisher} * @param source7 - * a seventh source Publisher + * a seventh source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Publisher source4, Publisher source5, Publisher source6, - Publisher source7, - Function7 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Publisher source4, @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Publisher source7, + @NonNull Function7 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6, source7); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * eight items emitted, in sequence, by eight other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * eight items emitted, in sequence, by eight other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -5195,7 +5684,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -5211,61 +5700,64 @@ public static Flowable zip( * @param the value type of the eighth source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param source4 - * a fourth source Publisher + * a fourth source {@code Publisher} * @param source5 - * a fifth source Publisher + * a fifth source {@code Publisher} * @param source6 - * a sixth source Publisher + * a sixth source {@code Publisher} * @param source7 - * a seventh source Publisher + * a seventh source {@code Publisher} * @param source8 - * an eighth source Publisher + * an eighth source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Publisher source4, Publisher source5, Publisher source6, - Publisher source7, Publisher source8, - Function8 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Publisher source4, @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Publisher source7, @NonNull Publisher source8, + @NonNull Function8 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6, source7, source8); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * nine items emitted, in sequence, by nine other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * nine items emitted, in sequence, by nine other {@link Publisher}s. *

- * + * *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source Publisher that emits the fewest + * The resulting {@code Flowable} returned from {@code zip} will invoke {@link Subscriber#onNext onNext} + * as many times as the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest * items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if @@ -5282,7 +5774,7 @@ public static Flowable zip( *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -5299,63 +5791,67 @@ public static Flowable zip( * @param the value type of the ninth source * @param the zipped result type * @param source1 - * the first source Publisher + * the first source {@code Publisher} * @param source2 - * a second source Publisher + * a second source {@code Publisher} * @param source3 - * a third source Publisher + * a third source {@code Publisher} * @param source4 - * a fourth source Publisher + * a fourth source {@code Publisher} * @param source5 - * a fifth source Publisher + * a fifth source {@code Publisher} * @param source6 - * a sixth source Publisher + * a sixth source {@code Publisher} * @param source7 - * a seventh source Publisher + * a seventh source {@code Publisher} * @param source8 - * an eighth source Publisher + * an eighth source {@code Publisher} * @param source9 - * a ninth source Publisher + * a ninth source {@code Publisher} * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher - * @return a Flowable that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8}, {@code source9} + * or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zip( - Publisher source1, Publisher source2, Publisher source3, - Publisher source4, Publisher source5, Publisher source6, - Publisher source7, Publisher source8, Publisher source9, - Function9 zipper) { - - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); - ObjectHelper.requireNonNull(source9, "source9 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> Flowable zip( + @NonNull Publisher source1, @NonNull Publisher source2, @NonNull Publisher source3, + @NonNull Publisher source4, @NonNull Publisher source5, @NonNull Publisher source6, + @NonNull Publisher source7, @NonNull Publisher source8, @NonNull Publisher source9, + @NonNull Function9 zipper) { + + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6, source7, source8, source9); } /** - * Returns a Flowable that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an array of other Publishers. + * Returns a {@code Flowable} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an array of other {@link Publisher}s. *

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

- * The resulting {@code Publisher} returned from {@code zip} will invoke {@code onNext} as many times as - * the number of {@code onNext} invocations of the source Publisher that emits the fewest items. + * The resulting {@code Flowable} returned from {@code zip} will invoke {@code onNext} as many times as + * the number of {@code onNext} invocations of the source {@code Publisher} that emits the fewest items. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if * one of the sources is shorter than the rest while canceling the other sources. Therefore, it @@ -5370,11 +5866,11 @@ public static Flowable zip( * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zipArray} does not operate by default on a particular {@link Scheduler}.
@@ -5383,29 +5879,33 @@ public static Flowable zip( * @param the common element type * @param the result type * @param sources - * an array of source Publishers + * an array of source {@code Publisher}s * @param zipper - * a function that, when applied to an item emitted by each of the source Publishers, results in - * an item that will be emitted by the resulting Publisher + * a function that, when applied to an item emitted by each of the source {@code Publisher}s, results in + * an item that will be emitted by the resulting {@code Flowable} * @param delayError - * delay errors signaled by any of the source Publisher until all Publishers terminate + * delay errors signaled by any of the source {@code Publisher} until all {@code Publisher}s terminate * @param bufferSize - * the number of elements to prefetch from each source Publisher - * @return a Flowable that emits the zipped results + * the number of elements to prefetch from each source {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable zipArray(Function zipper, - boolean delayError, int bufferSize, Publisher... sources) { + @SafeVarargs + public static <@NonNull T, @NonNull R> Flowable zipArray(@NonNull Function zipper, + boolean delayError, int bufferSize, @NonNull Publisher... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } - ObjectHelper.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(zipper, "zipper is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableZip(sources, null, zipper, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new FlowableZip<>(sources, null, zipper, bufferSize, delayError)); } // *************************************************************************************************** @@ -5413,101 +5913,109 @@ public static Flowable zipArray(Function - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code all} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates an item and returns a Boolean - * @return a Single that emits {@code true} if all items emitted by the source Publisher satisfy the - * predicate; otherwise, {@code false} + * a function that evaluates an item and returns a {@code Boolean} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: All */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single all(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableAllSingle(this, predicate)); + public final Single all(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new FlowableAllSingle<>(this, predicate)); } /** - * Mirrors the Publisher (current or provided) that first either emits an item or sends a termination + * Mirrors the {@link Publisher} (current or provided) that first either emits an item or sends a termination * notification. *

- * + * + *

+ * When the current {@code Flowable} signals an item or terminates first, the subscription to the other + * {@code Publisher} is canceled. If the other {@code Publisher} signals an item or terminates first, + * the subscription to the current {@code Flowable} is canceled. *

*
Backpressure:
*
The operator itself doesn't interfere with backpressure which is determined by the winning * {@code Publisher}'s backpressure behavior.
*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If the losing {@code Publisher} signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param other - * a Publisher competing to react first. A subscription to this provided Publisher will occur after subscribing - * to the current Publisher. - * @return a Flowable that emits the same sequence as whichever of the source Publishers first - * emitted an item or sent a termination notification + * a {@code Publisher} competing to react first. A subscription to this provided {@code Publisher} will occur after subscribing + * to the current {@code Flowable}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Amb */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable ambWith(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Flowable ambWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); return ambArray(this, other); } /** - * Returns a Single that emits {@code true} if any item emitted by the source Publisher satisfies a + * Returns a {@link Single} that emits {@code true} if any item emitted by the current {@code Flowable} satisfies a * specified condition, otherwise {@code false}. Note: this always emits {@code false} if the - * source Publisher is empty. + * current {@code Flowable} is empty. *

- * + * *

* In Rx.Net this is the {@code any} operator but we renamed it in RxJava to better match Java naming * idioms. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded manner + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code any} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * the condition to test items emitted by the source Publisher - * @return a Single that emits a Boolean that indicates whether any item emitted by the source - * Publisher satisfies the {@code predicate} + * the condition to test items emitted by the current {@code Flowable} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single any(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableAnySingle(this, predicate)); + public final Single any(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new FlowableAnySingle<>(this, predicate)); } /** * Returns the first item emitted by this {@code Flowable}, or throws - * {@code NoSuchElementException} if it emits no items. + * {@link NoSuchElementException} if it emits no items. *
*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingFirst} does not operate by default on a particular {@link Scheduler}.
@@ -5517,7 +6025,7 @@ public final Single any(Predicate predicate) { * {@link Error}s are rethrown as they are. *
* - * @return the first item emitted by this {@code Flowable} + * @return the new {@code Flowable} instance * @throws NoSuchElementException * if this {@code Flowable} emits no items * @see ReactiveX documentation: First @@ -5525,8 +6033,9 @@ public final Single any(Predicate predicate) { @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final T blockingFirst() { - BlockingFirstSubscriber s = new BlockingFirstSubscriber(); + BlockingFirstSubscriber s = new BlockingFirstSubscriber<>(); subscribe(s); T v = s.blockingGet(); if (v != null) { @@ -5540,7 +6049,7 @@ public final T blockingFirst() { * items. *
*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingFirst} does not operate by default on a particular {@link Scheduler}.
@@ -5552,26 +6061,28 @@ public final T blockingFirst() { * * @param defaultItem * a default value to return if this {@code Flowable} emits no items - * @return the first item emitted by this {@code Flowable}, or the default value if it emits no - * items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingFirst(T defaultItem) { - BlockingFirstSubscriber s = new BlockingFirstSubscriber(); + @NonNull + public final T blockingFirst(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + BlockingFirstSubscriber s = new BlockingFirstSubscriber<>(); subscribe(s); T v = s.blockingGet(); return v != null ? v : defaultItem; } /** - * Consumes the upstream {@code Flowable} in a blocking fashion and invokes the given - * {@code Consumer} with each upstream item on the current thread until the + * Consumes the current {@code Flowable} in a blocking fashion and invokes the given + * {@link Consumer} with each upstream item on the current thread until the * upstream terminates. *

- * + * *

* Note: the method will only return if the upstream terminates or the current * thread is interrupted. @@ -5581,8 +6092,8 @@ public final T blockingFirst(T defaultItem) { * sequence. *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner - * (i.e., no backpressure applied to it).
+ *
The operator requests {@link Flowable#bufferSize()} upfront, then 75% of this + * amount when 75% is received.
*
Scheduler:
*
{@code blockingForEach} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
@@ -5592,16 +6103,63 @@ public final T blockingFirst(T defaultItem) { *
* * @param onNext - * the {@link Consumer} to invoke for each item emitted by the {@code Flowable} + * the {@code Consumer} to invoke for each item emitted by the {@code Flowable} + * @throws NullPointerException if {@code onNext} is {@code null} * @throws RuntimeException - * if an error occurs + * if an error occurs; {@code Error}s and {@code RuntimeException}s are rethrown + * as they are, checked {@code Exception}s are wrapped into {@code RuntimeException}s * @see ReactiveX documentation: Subscribe * @see #subscribe(Consumer) + * @see #blockingForEach(Consumer, int) */ - @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingForEach(@NonNull Consumer onNext) { + blockingForEach(onNext, bufferSize()); + } + + /** + * Consumes the current {@code Flowable} in a blocking fashion and invokes the given + * {@link Consumer} with each upstream item on the current thread until the + * upstream terminates. + *

+ * + *

+ * Note: the method will only return if the upstream terminates or the current + * thread is interrupted. + *

+ * This method executes the {@code Consumer} on the current thread while + * {@link #subscribe(Consumer)} executes the consumer on the original caller thread of the + * sequence. + *

+ *
Backpressure:
+ *
The operator requests the given {@code prefetch} amount upfront, then 75% of this + * amount when 75% is received.
+ *
Scheduler:
+ *
{@code blockingForEach} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the source signals an error, the operator wraps a checked {@link Exception} + * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and + * {@link Error}s are rethrown as they are.
+ *
+ * + * @param onNext + * the {@code Consumer} to invoke for each item emitted by the {@code Flowable} + * @param bufferSize + * the number of items to prefetch upfront, then 75% of it after 75% received + * @throws NullPointerException if {@code onNext} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive + * @throws RuntimeException + * if an error occurs; {@code Error}s and {@code RuntimeException}s are rethrown + * as they are, checked {@code Exception}s are wrapped into {@code RuntimeException}s + * @see ReactiveX documentation: Subscribe + * @see #subscribe(Consumer) + */ + @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingForEach(Consumer onNext) { - Iterator it = blockingIterable().iterator(); + public final void blockingForEach(@NonNull Consumer onNext, int bufferSize) { + Objects.requireNonNull(onNext, "onNext is null"); + Iterator it = blockingIterable(bufferSize).iterator(); while (it.hasNext()) { try { onNext.accept(it.next()); @@ -5616,21 +6174,22 @@ public final void blockingForEach(Consumer onNext) { /** * Converts this {@code Flowable} into an {@link Iterable}. *

- * + * *

*
Backpressure:
*
The operator expects the upstream to honor backpressure otherwise the returned - * Iterable's iterator will throw a {@code MissingBackpressureException}.
+ * {@code Iterable}'s iterator will throw a {@link MissingBackpressureException}. *
Scheduler:
*
{@code blockingIterable} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@link Iterable} version of this {@code Flowable} + * @return the new {@code Iterable} instance * @see ReactiveX documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingIterable() { return blockingIterable(bufferSize()); } @@ -5638,36 +6197,38 @@ public final Iterable blockingIterable() { /** * Converts this {@code Flowable} into an {@link Iterable}. *

- * + * *

*
Backpressure:
*
The operator expects the upstream to honor backpressure otherwise the returned - * Iterable's iterator will throw a {@code MissingBackpressureException}. + * {@code Iterable}'s iterator will throw a {@link MissingBackpressureException}. *
*
Scheduler:
*
{@code blockingIterable} does not operate by default on a particular {@link Scheduler}.
*
* - * @param bufferSize the number of items to prefetch from the current Flowable - * @return an {@link Iterable} version of this {@code Flowable} + * @param bufferSize the number of items to prefetch from the current {@code Flowable} + * @return the new {@code Iterable} instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingIterable(int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return new BlockingFlowableIterable(this, bufferSize); + return new BlockingFlowableIterable<>(this, bufferSize); } /** * Returns the last item emitted by this {@code Flowable}, or throws - * {@code NoSuchElementException} if this {@code Flowable} emits no items. + * {@link NoSuchElementException} if this {@code Flowable} emits no items. *

- * + * *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingLast} does not operate by default on a particular {@link Scheduler}.
@@ -5677,7 +6238,7 @@ public final Iterable blockingIterable(int bufferSize) { * {@link Error}s are rethrown as they are. *
* - * @return the last item emitted by this {@code Flowable} + * @return the new {@code Flowable} instance * @throws NoSuchElementException * if this {@code Flowable} emits no items * @see ReactiveX documentation: Last @@ -5685,8 +6246,9 @@ public final Iterable blockingIterable(int bufferSize) { @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final T blockingLast() { - BlockingLastSubscriber s = new BlockingLastSubscriber(); + BlockingLastSubscriber s = new BlockingLastSubscriber<>(); subscribe(s); T v = s.blockingGet(); if (v != null) { @@ -5699,10 +6261,10 @@ public final T blockingLast() { * Returns the last item emitted by this {@code Flowable}, or a default value if it emits no * items. *

- * + * *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingLast} does not operate by default on a particular {@link Scheduler}.
@@ -5714,15 +6276,17 @@ public final T blockingLast() { * * @param defaultItem * a default value to return if this {@code Flowable} emits no items - * @return the last item emitted by the {@code Flowable}, or the default value if it emits no - * items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX documentation: Last */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingLast(T defaultItem) { - BlockingLastSubscriber s = new BlockingLastSubscriber(); + @NonNull + public final T blockingLast(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + BlockingLastSubscriber s = new BlockingLastSubscriber<>(); subscribe(s); T v = s.blockingGet(); return v != null ? v : defaultItem; @@ -5739,81 +6303,84 @@ public final T blockingLast(T defaultItem) { * event. *
*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingLatest} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an Iterable that always returns the latest item emitted by this {@code Flowable} + * @return the new {@code Iterable} instance * @see ReactiveX documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingLatest() { - return new BlockingFlowableLatest(this); + return new BlockingFlowableLatest<>(this); } /** * Returns an {@link Iterable} that always returns the item most recently emitted by this * {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingMostRecent} does not operate by default on a particular {@link Scheduler}.
*
* * @param initialItem - * the initial item that the {@link Iterable} sequence will yield if this + * the initial item that the {@code Iterable} sequence will yield if this * {@code Flowable} has not yet emitted an item - * @return an {@link Iterable} that on each iteration returns the item that this {@code Flowable} - * has most recently emitted + * @return the new {@code Iterable} instance + * @throws NullPointerException if {@code initialItem} is {@code null} * @see ReactiveX documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Iterable blockingMostRecent(T initialItem) { - return new BlockingFlowableMostRecent(this, initialItem); + @NonNull + public final Iterable blockingMostRecent(@NonNull T initialItem) { + Objects.requireNonNull(initialItem, "initialItem is null"); + return new BlockingFlowableMostRecent<>(this, initialItem); } /** * Returns an {@link Iterable} that blocks until this {@code Flowable} emits another item, then * returns that item. *

- * + * *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingNext} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@link Iterable} that blocks upon each iteration until this {@code Flowable} emits - * a new item, whereupon the Iterable returns that item + * @return the new {@code Iterable} instance * @see ReactiveX documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingNext() { - return new BlockingFlowableNext(this); + return new BlockingFlowableNext<>(this); } /** * If this {@code Flowable} completes after emitting a single item, return that item, otherwise - * throw a {@code NoSuchElementException}. + * throw a {@link NoSuchElementException}. *

- * + * *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingSingle} does not operate by default on a particular {@link Scheduler}.
@@ -5823,25 +6390,26 @@ public final Iterable blockingNext() { * {@link Error}s are rethrown as they are. *
* - * @return the single item emitted by this {@code Flowable} + * @return the new {@code Flowable} instance * @see ReactiveX documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final T blockingSingle() { return singleOrError().blockingGet(); } /** * If this {@code Flowable} completes after emitting a single item, return that item; if it emits - * more than one item, throw an {@code IllegalArgumentException}; if it emits no items, return a default + * more than one item, throw an {@link IllegalArgumentException}; if it emits no items, return a default * value. *

- * + * *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingSingle} does not operate by default on a particular {@link Scheduler}.
@@ -5853,55 +6421,57 @@ public final T blockingSingle() { * * @param defaultItem * a default value to return if this {@code Flowable} emits no items - * @return the single item emitted by this {@code Flowable}, or the default value if it emits no - * items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingSingle(T defaultItem) { + @NonNull + public final T blockingSingle(@NonNull T defaultItem) { return single(defaultItem).blockingGet(); } /** * Returns a {@link Future} representing the only value emitted by this {@code Flowable}. *

- * + * *

- * If the {@link Flowable} emits more than one item, {@link java.util.concurrent.Future} will receive an - * {@link java.lang.IndexOutOfBoundsException}. If the {@link Flowable} is empty, {@link java.util.concurrent.Future} + * If the {@code Flowable} emits more than one item, {@link java.util.concurrent.Future} will receive an + * {@link java.lang.IndexOutOfBoundsException}. If the {@code Flowable} is empty, {@link java.util.concurrent.Future} * will receive a {@link java.util.NoSuchElementException}. The {@code Flowable} source has to terminate in order * for the returned {@code Future} to terminate as well. *

* If the {@code Flowable} may emit more than one item, use {@code Flowable.toList().toFuture()}. *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code toFuture} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Future} that expects a single item to be emitted by this {@code Flowable} + * @return the new {@code Future} instance * @see ReactiveX documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Future toFuture() { - return subscribeWith(new FutureSubscriber()); + return subscribeWith(new FutureSubscriber<>()); } /** - * Runs the source Flowable to a terminal event, ignoring any values and rethrowing any exception. + * Runs the current {@code Flowable} to a terminal event, ignoring any values and rethrowing any exception. *

* Note that calling this method will block the caller thread until the upstream terminates * normally or with an error. Therefore, calling this method from special threads such as the * Android Main Thread or the Swing Event Dispatch Thread is not recommended. *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
@@ -5920,9 +6490,9 @@ public final void blockingSubscribe() { /** * Subscribes to the source and calls the given callbacks on the current thread. *

- * If the Flowable emits an error, it is wrapped into an + * If the {@code Flowable} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. * Using the overloads {@link #blockingSubscribe(Consumer, Consumer)} * or {@link #blockingSubscribe(Consumer, Consumer, Action)} instead is recommended. *

@@ -5931,28 +6501,29 @@ public final void blockingSubscribe() { * Android Main Thread or the Swing Event Dispatch Thread is not recommended. *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
*
* @param onNext the callback action for each source value + * @throws NullPointerException if {@code onNext} is {@code null} * @since 2.0 * @see #blockingSubscribe(Consumer, Consumer) * @see #blockingSubscribe(Consumer, Consumer, Action) */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Consumer onNext) { + public final void blockingSubscribe(@NonNull Consumer onNext) { FlowableBlockingSubscribe.subscribe(this, onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** * Subscribes to the source and calls the given callbacks on the current thread. *

- * If the Flowable emits an error, it is wrapped into an + * If the {@code Flowable} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. * Using the overloads {@link #blockingSubscribe(Consumer, Consumer)} * or {@link #blockingSubscribe(Consumer, Consumer, Action)} instead is recommended. *

@@ -5961,7 +6532,7 @@ public final void blockingSubscribe(Consumer onNext) { * Android Main Thread or the Swing Event Dispatch Thread is not recommended. *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an bounded manner (up to bufferSize + *
The operator consumes the current {@code Flowable} in an bounded manner (up to bufferSize * outstanding request amount for items).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
@@ -5969,13 +6540,15 @@ public final void blockingSubscribe(Consumer onNext) { *

History: 2.1.15 - experimental * @param onNext the callback action for each source value * @param bufferSize the size of the buffer + * @throws NullPointerException if {@code onNext} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see #blockingSubscribe(Consumer, Consumer) * @see #blockingSubscribe(Consumer, Consumer, Action) * @since 2.2 */ @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Consumer onNext, int bufferSize) { + public final void blockingSubscribe(@NonNull Consumer onNext, int bufferSize) { FlowableBlockingSubscribe.subscribe(this, onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION, bufferSize); } @@ -5987,19 +6560,20 @@ public final void blockingSubscribe(Consumer onNext, int bufferSize) * Android Main Thread or the Swing Event Dispatch Thread is not recommended. *

*
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
*
* @param onNext the callback action for each source value * @param onError the callback action for an error event + * @throws NullPointerException if {@code onNext} or {@code onError} is {@code null} * @since 2.0 * @see #blockingSubscribe(Consumer, Consumer, Action) */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Consumer onNext, Consumer onError) { + public final void blockingSubscribe(@NonNull Consumer onNext, @NonNull Consumer onError) { FlowableBlockingSubscribe.subscribe(this, onNext, onError, Functions.EMPTY_ACTION); } @@ -6011,7 +6585,7 @@ public final void blockingSubscribe(Consumer onNext, Consumer *
Backpressure:
- *
The operator consumes the source {@code Flowable} in an bounded manner (up to bufferSize + *
The operator consumes the current {@code Flowable} in an bounded manner (up to bufferSize * outstanding request amount for items).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
@@ -6020,12 +6594,14 @@ public final void blockingSubscribe(Consumer onNext, Consumer onNext, Consumer onError, + public final void blockingSubscribe(@NonNull Consumer onNext, @NonNull Consumer onError, int bufferSize) { FlowableBlockingSubscribe.subscribe(this, onNext, onError, Functions.EMPTY_ACTION, bufferSize); } @@ -6038,7 +6614,7 @@ public final void blockingSubscribe(Consumer onNext, Consumer *
Backpressure:
- *
The operator consumes the source {@code Flowable} in an unbounded manner + *
The operator consumes the current {@code Flowable} in an unbounded manner * (i.e., no backpressure applied to it).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
@@ -6046,11 +6622,12 @@ public final void blockingSubscribe(Consumer onNext, Consumer onNext, Consumer onError, Action onComplete) { + public final void blockingSubscribe(@NonNull Consumer onNext, @NonNull Consumer onError, @NonNull Action onComplete) { FlowableBlockingSubscribe.subscribe(this, onNext, onError, onComplete); } @@ -6062,7 +6639,7 @@ public final void blockingSubscribe(Consumer onNext, Consumer *
Backpressure:
- *
The operator consumes the source {@code Flowable} in an bounded manner (up to bufferSize + *
The operator consumes the current {@code Flowable} in an bounded manner (up to bufferSize * outstanding request amount for items).
*
Scheduler:
*
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
@@ -6072,12 +6649,14 @@ public final void blockingSubscribe(Consumer onNext, Consumer onNext, Consumer onError, Action onComplete, - int bufferSize) { + public final void blockingSubscribe(@NonNull Consumer onNext, @NonNull Consumer onError, @NonNull Action onComplete, + int bufferSize) { FlowableBlockingSubscribe.subscribe(this, onNext, onError, onComplete, bufferSize); } @@ -6097,26 +6676,28 @@ public final void blockingSubscribe(Consumer onNext, Consumer * The cancellation and backpressure is composed through. * @param subscriber the subscriber to forward events and calls to in the current thread + * @throws NullPointerException if {@code subscriber} is {@code null} * @since 2.0 */ @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Subscriber subscriber) { + public final void blockingSubscribe(@NonNull Subscriber subscriber) { + Objects.requireNonNull(subscriber, "subscriber is null"); FlowableBlockingSubscribe.subscribe(this, subscriber); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each containing {@code count} items. When the source - * Publisher completes, the resulting Publisher emits the current buffer and propagates the notification from the - * source Publisher. Note that if the source Publisher issues an onError notification the event is passed on + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each containing {@code count} items. When the current + * {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer and propagates the notification from the + * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and expects the source {@code Publisher} to honor it as - * well, although not enforced; violation may lead to {@code MissingBackpressureException} somewhere + *
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as + * well, although not enforced; violation may lead to {@link MissingBackpressureException} somewhere * downstream.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6124,29 +6705,30 @@ public final void blockingSubscribe(Subscriber subscriber) { * * @param count * the maximum number of items in each buffer before it should be emitted - * @return a Flowable that emits connected, non-overlapping buffers, each containing at most - * {@code count} items from the source Publisher + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> buffer(int count) { return buffer(count, count); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits buffers every {@code skip} items, each containing {@code count} items. When the source - * Publisher completes, the resulting Publisher emits the current buffer and propagates the notification from the - * source Publisher. Note that if the source Publisher issues an onError notification the event is passed on + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits buffers every {@code skip} items, each containing {@code count} items. When the current + * {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer and propagates the notification from the + * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and expects the source {@code Publisher} to honor it as - * well, although not enforced; violation may lead to {@code MissingBackpressureException} somewhere + *
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as + * well, although not enforced; violation may lead to {@link MissingBackpressureException} somewhere * downstream.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6155,32 +6737,33 @@ public final Flowable> buffer(int count) { * @param count * the maximum size of each buffer before it should be emitted * @param skip - * how many items emitted by the source Publisher should be skipped before starting a new + * how many items emitted by the current {@code Flowable} should be skipped before starting a new * buffer. Note that when {@code skip} and {@code count} are equal, this is the same operation as * {@link #buffer(int)}. - * @return a Flowable that emits buffers for every {@code skip} item from the source Publisher and - * containing at most {@code count} items + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code count} or {@code skip} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> buffer(int count, int skip) { - return buffer(count, skip, ArrayListSupplier.asSupplier()); + return buffer(count, skip, ArrayListSupplier.asSupplier()); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits buffers every {@code skip} items, each containing {@code count} items. When the source - * Publisher completes, the resulting Publisher emits the current buffer and propagates the notification from the - * source Publisher. Note that if the source Publisher issues an onError notification the event is passed on + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits buffers every {@code skip} items, each containing {@code count} items. When the current + * {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer and propagates the notification from the + * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and expects the source {@code Publisher} to honor it as - * well, although not enforced; violation may lead to {@code MissingBackpressureException} somewhere + *
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as + * well, although not enforced; violation may lead to {@link MissingBackpressureException} somewhere * downstream.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6190,39 +6773,40 @@ public final Flowable> buffer(int count, int skip) { * @param count * the maximum size of each buffer before it should be emitted * @param skip - * how many items emitted by the source Publisher should be skipped before starting a new + * how many items emitted by the current {@code Flowable} should be skipped before starting a new * buffer. Note that when {@code skip} and {@code count} are equal, this is the same operation as * {@link #buffer(int)}. * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return a Flowable that emits buffers for every {@code skip} item from the source Publisher and - * containing at most {@code count} items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code bufferSupplier} is {@code null} + * @throws IllegalArgumentException if {@code count} or {@code skip} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final > Flowable buffer(int count, int skip, Supplier bufferSupplier) { + public final <@NonNull U extends Collection> Flowable buffer(int count, int skip, @NonNull Supplier bufferSupplier) { ObjectHelper.verifyPositive(count, "count"); ObjectHelper.verifyPositive(skip, "skip"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); - return RxJavaPlugins.onAssembly(new FlowableBuffer(this, count, skip, bufferSupplier)); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableBuffer<>(this, count, skip, bufferSupplier)); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each containing {@code count} items. When the source - * Publisher completes, the resulting Publisher emits the current buffer and propagates the notification from the - * source Publisher. Note that if the source Publisher issues an onError notification the event is passed on + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each containing {@code count} items. When the current + * {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer and propagates the notification from the + * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and expects the source {@code Publisher} to honor it as - * well, although not enforced; violation may lead to {@code MissingBackpressureException} somewhere + *
The operator honors backpressure from downstream and expects the current {@code Flowable} to honor it as + * well, although not enforced; violation may lead to {@link MissingBackpressureException} somewhere * downstream.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6234,29 +6818,31 @@ public final > Flowable buffer(int count, int * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return a Flowable that emits connected, non-overlapping buffers, each containing at most - * {@code count} items from the source Publisher + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code bufferSupplier} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final > Flowable buffer(int count, Supplier bufferSupplier) { + @NonNull + public final <@NonNull U extends Collection> Flowable buffer(int count, @NonNull Supplier bufferSupplier) { return buffer(count, count, bufferSupplier); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher starts a new buffer periodically, as determined by the {@code timeskip} argument. It emits - * each buffer after a fixed timespan, specified by the {@code timespan} argument. When the source - * Publisher completes, the resulting Publisher emits the current buffer and propagates the notification from the - * source Publisher. Note that if the source Publisher issues an onError notification the event is passed on + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} starts a new buffer periodically, as determined by the {@code timeskip} argument. It emits + * each buffer after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer and propagates the notification from the + * current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -6268,30 +6854,31 @@ public final > Flowable buffer(int count, Sup * the period of time after which a new buffer will be created * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments - * @return a Flowable that emits new buffers of items emitted by the source Publisher periodically after - * a fixed timespan has elapsed + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> buffer(long timespan, long timeskip, TimeUnit unit) { - return buffer(timespan, timeskip, unit, Schedulers.computation(), ArrayListSupplier.asSupplier()); + @NonNull + public final Flowable> buffer(long timespan, long timeskip, @NonNull TimeUnit unit) { + return buffer(timespan, timeskip, unit, Schedulers.computation(), ArrayListSupplier.asSupplier()); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the * specified {@code scheduler}. It emits each buffer after a fixed timespan, specified by the - * {@code timespan} argument. When the source Publisher completes, the resulting Publisher emits the current buffer - * and propagates the notification from the source Publisher. Note that if the source Publisher issues an onError + * {@code timespan} argument. When the current {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer + * and propagates the notification from the current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} * notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6304,31 +6891,32 @@ public final Flowable> buffer(long timespan, long timeskip, TimeUnit uni * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer - * @return a Flowable that emits new buffers of items emitted by the source Publisher periodically after - * a fixed timespan has elapsed + * the {@code Scheduler} to use when determining the end and start of a buffer + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> buffer(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler) { - return buffer(timespan, timeskip, unit, scheduler, ArrayListSupplier.asSupplier()); + @NonNull + public final Flowable> buffer(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return buffer(timespan, timeskip, unit, scheduler, ArrayListSupplier.asSupplier()); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the * specified {@code scheduler}. It emits each buffer after a fixed timespan, specified by the - * {@code timespan} argument. When the source Publisher completes, the resulting Publisher emits the current buffer - * and propagates the notification from the source Publisher. Note that if the source Publisher issues an onError + * {@code timespan} argument. When the current {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer + * and propagates the notification from the current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} * notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6342,38 +6930,38 @@ public final Flowable> buffer(long timespan, long timeskip, TimeUnit uni * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer + * the {@code Scheduler} to use when determining the end and start of a buffer * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return a Flowable that emits new buffers of items emitted by the source Publisher periodically after - * a fixed timespan has elapsed + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code bufferSupplier} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final > Flowable buffer(long timespan, long timeskip, TimeUnit unit, - Scheduler scheduler, Supplier bufferSupplier) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); - return RxJavaPlugins.onAssembly(new FlowableBufferTimed(this, timespan, timeskip, unit, scheduler, bufferSupplier, Integer.MAX_VALUE, false)); + public final <@NonNull U extends Collection> Flowable buffer(long timespan, long timeskip, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, @NonNull Supplier bufferSupplier) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableBufferTimed<>(this, timespan, timeskip, unit, scheduler, bufferSupplier, Integer.MAX_VALUE, false)); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each of a fixed duration specified by the - * {@code timespan} argument. When the source Publisher completes, the resulting Publisher emits the current buffer - * and propagates the notification from the source Publisher. Note that if the source Publisher issues an onError + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each of a fixed duration specified by the + * {@code timespan} argument. When the current {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer + * and propagates the notification from the current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} * notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -6384,29 +6972,30 @@ public final > Flowable buffer(long timespan, * buffer * @param unit * the unit of time that applies to the {@code timespan} argument - * @return a Flowable that emits connected, non-overlapping buffers of items emitted by the source - * Publisher within a fixed duration + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> buffer(long timespan, TimeUnit unit) { + @NonNull + public final Flowable> buffer(long timespan, @NonNull TimeUnit unit) { return buffer(timespan, unit, Schedulers.computation(), Integer.MAX_VALUE); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each of a fixed duration specified by the + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source Publisher completes, the resulting Publisher emits the current buffer and propagates the - * notification from the source Publisher. Note that if the source Publisher issues an onError notification the event + * first). When the current {@code Flowable} completes, the resulting {@code Flowable} emits the current buffer and propagates the + * notification from the current {@code Flowable}. Note that if the current {@code Flowable} issues an {@code onError} notification the event * is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -6419,31 +7008,32 @@ public final Flowable> buffer(long timespan, TimeUnit unit) { * the unit of time which applies to the {@code timespan} argument * @param count * the maximum size of each buffer before it is emitted - * @return a Flowable that emits connected, non-overlapping buffers of items emitted by the source - * Publisher, after a fixed duration or when the buffer reaches maximum capacity (whichever occurs - * first) + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> buffer(long timespan, TimeUnit unit, int count) { + @NonNull + public final Flowable> buffer(long timespan, @NonNull TimeUnit unit, int count) { return buffer(timespan, unit, Schedulers.computation(), count); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each of a fixed duration specified by the + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each of a fixed duration specified by the * {@code timespan} argument as measured on the specified {@code scheduler}, or a maximum size specified by - * the {@code count} argument (whichever is reached first). When the source Publisher completes, the resulting - * Publisher emits the current buffer and propagates the notification from the source Publisher. Note that if the - * source Publisher issues an onError notification the event is passed on immediately without first emitting the + * the {@code count} argument (whichever is reached first). When the current {@code Flowable} completes, the resulting + * {@code Flowable} emits the current buffer and propagates the notification from the current {@code Flowable}. Note that if the + * current {@code Flowable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6455,34 +7045,35 @@ public final Flowable> buffer(long timespan, TimeUnit unit, int count) { * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer + * the {@code Scheduler} to use when determining the end and start of a buffer * @param count * the maximum size of each buffer before it is emitted - * @return a Flowable that emits connected, non-overlapping buffers of items emitted by the source - * Publisher after a fixed duration or when the buffer reaches maximum capacity (whichever occurs - * first) + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> buffer(long timespan, TimeUnit unit, Scheduler scheduler, int count) { - return buffer(timespan, unit, scheduler, count, ArrayListSupplier.asSupplier(), false); + @NonNull + public final Flowable> buffer(long timespan, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, int count) { + return buffer(timespan, unit, scheduler, count, ArrayListSupplier.asSupplier(), false); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each of a fixed duration specified by the + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each of a fixed duration specified by the * {@code timespan} argument as measured on the specified {@code scheduler}, or a maximum size specified by - * the {@code count} argument (whichever is reached first). When the source Publisher completes, the resulting - * Publisher emits the current buffer and propagates the notification from the source Publisher. Note that if the - * source Publisher issues an onError notification the event is passed on immediately without first emitting the + * the {@code count} argument (whichever is reached first). When the current {@code Flowable} completes, the resulting + * {@code Flowable} emits the current buffer and propagates the notification from the current {@code Flowable}. Note that if the + * current {@code Flowable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6495,46 +7086,47 @@ public final Flowable> buffer(long timespan, TimeUnit unit, Scheduler sc * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer + * the {@code Scheduler} to use when determining the end and start of a buffer * @param count * the maximum size of each buffer before it is emitted * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @param restartTimerOnMaxSize if true the time window is restarted when the max capacity of the current buffer + * @param restartTimerOnMaxSize if {@code true}, the time window is restarted when the max capacity of the current buffer * is reached - * @return a Flowable that emits connected, non-overlapping buffers of items emitted by the source - * Publisher after a fixed duration or when the buffer reaches maximum capacity (whichever occurs - * first) + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code bufferSupplier} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final > Flowable buffer( - long timespan, TimeUnit unit, - Scheduler scheduler, int count, - Supplier bufferSupplier, + @NonNull + public final <@NonNull U extends Collection> Flowable buffer( + long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, int count, + @NonNull Supplier bufferSupplier, boolean restartTimerOnMaxSize) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); ObjectHelper.verifyPositive(count, "count"); - return RxJavaPlugins.onAssembly(new FlowableBufferTimed(this, timespan, timespan, unit, scheduler, bufferSupplier, count, restartTimerOnMaxSize)); + return RxJavaPlugins.onAssembly(new FlowableBufferTimed<>(this, timespan, timespan, unit, scheduler, bufferSupplier, count, restartTimerOnMaxSize)); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping buffers, each of a fixed duration specified by the - * {@code timespan} argument and on the specified {@code scheduler}. When the source Publisher completes, the - * resulting Publisher emits the current buffer and propagates the notification from the source Publisher. Note that - * if the source Publisher issues an onError notification the event is passed on immediately without first emitting + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping buffers, each of a fixed duration specified by the + * {@code timespan} argument and on the specified {@code scheduler}. When the current {@code Flowable} completes, the + * resulting {@code Flowable} emits the current buffer and propagates the notification from the current {@code Flowable}. Note that + * if the current {@code Flowable} issues an {@code onError} notification the event is passed on immediately without first emitting * the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it uses time. It requests {@code Long.MAX_VALUE} + *
This operator does not support backpressure as it uses time. It requests {@link Long#MAX_VALUE} * upstream and does not obey downstream requests.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -6546,111 +7138,114 @@ public final > Flowable buffer( * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer - * @return a Flowable that emits connected, non-overlapping buffers of items emitted by the source - * Publisher within a fixed duration + * the {@code Scheduler} to use when determining the end and start of a buffer + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { - return buffer(timespan, unit, scheduler, Integer.MAX_VALUE, ArrayListSupplier.asSupplier(), false); + @NonNull + public final Flowable> buffer(long timespan, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return buffer(timespan, unit, scheduler, Integer.MAX_VALUE, ArrayListSupplier.asSupplier(), false); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits buffers that it creates when the specified {@code openingIndicator} Publisher emits an - * item, and closes when the Publisher returned from {@code closingIndicator} emits an item. If any of the source - * Publisher, {@code openingIndicator} or {@code closingIndicator} issues an onError notification the event is passed + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits buffers that it creates when the specified {@code openingIndicator} {@link Publisher} emits an + * item, and closes when the {@code Publisher} returned from {@code closingIndicator} emits an item. If any of the current + * {@code PFlowable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the event is passed * on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it is instead controlled by the given Publishers and - * buffers data. It requests {@code Long.MAX_VALUE} upstream and does not obey downstream requests.
+ *
This operator does not support backpressure as it is instead controlled by the given {@code Publisher}s and + * buffers data. It requests {@link Long#MAX_VALUE} upstream and does not obey downstream requests.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the buffer-opening Publisher - * @param the element type of the individual buffer-closing Publishers + * @param the element type of the buffer-opening {@code Publisher} + * @param the element type of the individual buffer-closing {@code Publisher}s * @param openingIndicator - * the Publisher that, when it emits an item, causes a new buffer to be created + * the {@code Publisher} that, when it emits an item, causes a new buffer to be created * @param closingIndicator - * the {@link Function} that is used to produce a Publisher for every buffer created. When this - * Publisher emits an item, the associated buffer is emitted. - * @return a Flowable that emits buffers, containing items from the source Publisher, that are created - * and closed when the specified Publishers emit items + * the {@link Function} that is used to produce a {@code Publisher} for every buffer created. When this + * {@code Publisher} emits an item, the associated buffer is emitted. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code openingIndicator} or {@code closingIndicator} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> buffer( - Flowable openingIndicator, - Function> closingIndicator) { - return buffer(openingIndicator, closingIndicator, ArrayListSupplier.asSupplier()); + @NonNull + public final <@NonNull TOpening, @NonNull TClosing> Flowable> buffer( + @NonNull Publisher openingIndicator, + @NonNull Function> closingIndicator) { + return buffer(openingIndicator, closingIndicator, ArrayListSupplier.asSupplier()); } /** - * Returns a Flowable that emits buffers of items it collects from the source Publisher. The resulting - * Publisher emits buffers that it creates when the specified {@code openingIndicator} Publisher emits an - * item, and closes when the Publisher returned from {@code closingIndicator} emits an item. If any of the source - * Publisher, {@code openingIndicator} or {@code closingIndicator} issues an onError notification the event is passed + * Returns a {@code Flowable} that emits buffers of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits buffers that it creates when the specified {@code openingIndicator} {@link Publisher} emits an + * item, and closes when the {@code Publisher} returned from {@code closingIndicator} emits an item. If any of the current + * {@code Flowable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the event is passed * on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Backpressure:
- *
This operator does not support backpressure as it is instead controlled by the given Publishers and - * buffers data. It requests {@code Long.MAX_VALUE} upstream and does not obey downstream requests.
+ *
This operator does not support backpressure as it is instead controlled by the given {@code Publisher}s and + * buffers data. It requests {@link Long#MAX_VALUE} upstream and does not obey downstream requests.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
*
* * @param the collection subclass type to buffer into - * @param the element type of the buffer-opening Publisher - * @param the element type of the individual buffer-closing Publishers + * @param the element type of the buffer-opening {@code Publisher} + * @param the element type of the individual buffer-closing {@code Publisher}s * @param openingIndicator - * the Publisher that, when it emits an item, causes a new buffer to be created + * the {@code Publisher} that, when it emits an item, causes a new buffer to be created * @param closingIndicator - * the {@link Function} that is used to produce a Publisher for every buffer created. When this - * Publisher emits an item, the associated buffer is emitted. + * the {@link Function} that is used to produce a {@code Publisher} for every buffer created. When this + * {@code Publisher} emits an item, the associated buffer is emitted. * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return a Flowable that emits buffers, containing items from the source Publisher, that are created - * and closed when the specified Publishers emit items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code openingIndicator}, {@code closingIndicator} or {@code bufferSupplier} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final > Flowable buffer( - Flowable openingIndicator, - Function> closingIndicator, - Supplier bufferSupplier) { - ObjectHelper.requireNonNull(openingIndicator, "openingIndicator is null"); - ObjectHelper.requireNonNull(closingIndicator, "closingIndicator is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); + @NonNull + public final <@NonNull TOpening, @NonNull TClosing, @NonNull U extends Collection> Flowable buffer( + @NonNull Publisher openingIndicator, + @NonNull Function> closingIndicator, + @NonNull Supplier bufferSupplier) { + Objects.requireNonNull(openingIndicator, "openingIndicator is null"); + Objects.requireNonNull(closingIndicator, "closingIndicator is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); return RxJavaPlugins.onAssembly(new FlowableBufferBoundary(this, openingIndicator, closingIndicator, bufferSupplier)); } /** - * Returns a Flowable that emits non-overlapping buffered items from the source Publisher each time the - * specified boundary Publisher emits an item. + * Returns a {@code Flowable} that emits non-overlapping buffered items from the current {@code Flowable} each time the + * specified boundary {@link Publisher} emits an item. *

- * + * *

- * Completion of either the source or the boundary Publisher causes the returned Publisher to emit the - * latest buffer and complete. If either the source Publisher or the boundary Publisher issues an onError notification + * Completion of either the source or the boundary {@code Publisher} causes the returned {@code Publisher} to emit the + * latest buffer and complete. If either the current {@code Flowable} or the boundary {@code Publisher} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

*
Backpressure:
*
This operator does not support backpressure as it is instead controlled by the {@code Publisher} - * {@code boundary} and buffers data. It requests {@code Long.MAX_VALUE} upstream and does not obey + * {@code boundary} and buffers data. It requests {@link Long#MAX_VALUE} upstream and does not obey * downstream requests.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6659,32 +7254,33 @@ public final > Flowable b * @param * the boundary value type (ignored) * @param boundaryIndicator - * the boundary Publisher - * @return a Flowable that emits buffered items from the source Publisher when the boundary Publisher - * emits an item + * the boundary {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} * @see #buffer(Publisher, int) * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> buffer(Publisher boundaryIndicator) { - return buffer(boundaryIndicator, ArrayListSupplier.asSupplier()); + @NonNull + public final <@NonNull B> Flowable> buffer(@NonNull Publisher boundaryIndicator) { + return buffer(boundaryIndicator, ArrayListSupplier.asSupplier()); } /** - * Returns a Flowable that emits non-overlapping buffered items from the source Publisher each time the - * specified boundary Publisher emits an item. + * Returns a {@code Flowable} that emits non-overlapping buffered items from the current {@code Flowable} each time the + * specified boundary {@link Publisher} emits an item. *

- * + * *

- * Completion of either the source or the boundary Publisher causes the returned Publisher to emit the - * latest buffer and complete. If either the source Publisher or the boundary Publisher issues an onError notification + * Completion of either the source or the boundary {@code Publisher} causes the returned {@code Publisher} to emit the + * latest buffer and complete. If either the current {@code Flowable} or the boundary {@code Publisher} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

*
Backpressure:
*
This operator does not support backpressure as it is instead controlled by the {@code Publisher} - * {@code boundary} and buffers data. It requests {@code Long.MAX_VALUE} upstream and does not obey + * {@code boundary} and buffers data. It requests {@link Long#MAX_VALUE} upstream and does not obey * downstream requests.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6693,35 +7289,37 @@ public final Flowable> buffer(Publisher boundaryIndicator) { * @param * the boundary value type (ignored) * @param boundaryIndicator - * the boundary Publisher + * the boundary {@code Publisher} * @param initialCapacity * the initial capacity of each buffer chunk - * @return a Flowable that emits buffered items from the source Publisher when the boundary Publisher - * emits an item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} + * @throws IllegalArgumentException if {@code initialCapacity} is non-positive * @see ReactiveX operators documentation: Buffer * @see #buffer(Publisher) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> buffer(Publisher boundaryIndicator, final int initialCapacity) { + @NonNull + public final <@NonNull B> Flowable> buffer(@NonNull Publisher boundaryIndicator, int initialCapacity) { ObjectHelper.verifyPositive(initialCapacity, "initialCapacity"); - return buffer(boundaryIndicator, Functions.createArrayList(initialCapacity)); + return buffer(boundaryIndicator, Functions.createArrayList(initialCapacity)); } /** - * Returns a Flowable that emits non-overlapping buffered items from the source Publisher each time the - * specified boundary Publisher emits an item. + * Returns a {@code Flowable} that emits non-overlapping buffered items from the current {@code Flowable} each time the + * specified boundary {@link Publisher} emits an item. *

- * + * *

- * Completion of either the source or the boundary Publisher causes the returned Publisher to emit the - * latest buffer and complete. If either the source Publisher or the boundary Publisher issues an onError notification + * Completion of either the source or the boundary {@code Publisher} causes the returned {@code Publisher} to emit the + * latest buffer and complete. If either the current {@code Flowable} or the boundary {@code Publisher} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

*
Backpressure:
*
This operator does not support backpressure as it is instead controlled by the {@code Publisher} - * {@code boundary} and buffers data. It requests {@code Long.MAX_VALUE} upstream and does not obey + * {@code boundary} and buffers data. It requests {@link Long#MAX_VALUE} upstream and does not obey * downstream requests.
*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -6731,42 +7329,43 @@ public final Flowable> buffer(Publisher boundaryIndicator, final * @param * the boundary value type (ignored) * @param boundaryIndicator - * the boundary Publisher + * the boundary {@code Publisher} * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return a Flowable that emits buffered items from the source Publisher when the boundary Publisher - * emits an item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code boundaryIndicator} or {@code bufferSupplier} is {@code null} * @see #buffer(Publisher, int) * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final > Flowable buffer(Publisher boundaryIndicator, Supplier bufferSupplier) { - ObjectHelper.requireNonNull(boundaryIndicator, "boundaryIndicator is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); - return RxJavaPlugins.onAssembly(new FlowableBufferExactBoundary(this, boundaryIndicator, bufferSupplier)); + @NonNull + public final <@NonNull B, @NonNull U extends Collection> Flowable buffer(@NonNull Publisher boundaryIndicator, @NonNull Supplier bufferSupplier) { + Objects.requireNonNull(boundaryIndicator, "boundaryIndicator is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableBufferExactBoundary<>(this, boundaryIndicator, bufferSupplier)); } /** - * Returns a Flowable that subscribes to this Publisher lazily, caches all of its events + * Returns a {@code Flowable} that subscribes to this {@link Publisher} lazily, caches all of its events * and replays them, in the same order as received, to all the downstream subscribers. *

- * + * *

- * This is useful when you want a Publisher to cache responses and you can't control the + * This is useful when you want a {@code Publisher} to cache responses and you can't control the * subscribe/cancel behavior of all the {@link Subscriber}s. *

* The operator subscribes only when the first downstream subscriber subscribes and maintains - * a single subscription towards this Publisher. In contrast, the operator family of {@link #replay()} + * a single subscription towards this {@code Publisher}. In contrast, the operator family of {@link #replay()} * that return a {@link ConnectableFlowable} require an explicit call to {@link ConnectableFlowable#connect()}. *

* Note: You sacrifice the ability to cancel the origin when you use the {@code cache} - * Subscriber so be careful not to use this Subscriber on Publishers that emit an infinite or very large number + * operator so be careful not to use this operator on {@code Publisher}s that emit an infinite or very large number * of items that will use up memory. - * A possible workaround is to apply `takeUntil` with a predicate or - * another source before (and perhaps after) the application of cache(). + * A possible workaround is to apply {@link #takeUntil(Publisher)} with a predicate or + * another source before (and perhaps after) the application of {@code cache()}. *


      * AtomicBoolean shouldStop = new AtomicBoolean();
      *
@@ -6790,41 +7389,43 @@ public final > Flowable buffer(Publisher
*
*
Backpressure:
- *
The operator consumes this Publisher in an unbounded fashion but respects the backpressure - * of each downstream Subscriber individually.
+ *
The operator consumes this {@code Publisher} in an unbounded fashion but respects the backpressure + * of each downstream {@code Subscriber} individually.
*
Scheduler:
*
{@code cache} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that, when first subscribed to, caches all of its items and notifications for the - * benefit of subsequent subscribers + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Replay + * @see #takeUntil(Predicate) + * @see #takeUntil(Publisher) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable cache() { return cacheWithInitialCapacity(16); } /** - * Returns a Flowable that subscribes to this Publisher lazily, caches all of its events + * Returns a {@code Flowable} that subscribes to this {@link Publisher} lazily, caches all of its events * and replays them, in the same order as received, to all the downstream subscribers. *

- * + * *

- * This is useful when you want a Publisher to cache responses and you can't control the + * This is useful when you want a {@code Publisher} to cache responses and you can't control the * subscribe/cancel behavior of all the {@link Subscriber}s. *

* The operator subscribes only when the first downstream subscriber subscribes and maintains - * a single subscription towards this Publisher. In contrast, the operator family of {@link #replay()} + * a single subscription towards this {@code Publisher}. In contrast, the operator family of {@link #replay()} * that return a {@link ConnectableFlowable} require an explicit call to {@link ConnectableFlowable#connect()}. *

* Note: You sacrifice the ability to cancel the origin when you use the {@code cache} - * Subscriber so be careful not to use this Subscriber on Publishers that emit an infinite or very large number + * operator so be careful not to use this operator on {@code Publisher}s that emit an infinite or very large number * of items that will use up memory. - * A possible workaround is to apply `takeUntil` with a predicate or - * another source before (and perhaps after) the application of cache(). + * A possible workaround is to apply {@link #takeUntil(Publisher)} with a predicate or + * another source before (and perhaps after) the application of {@code cacheWithInitialCapacity()}. *


      * AtomicBoolean shouldStop = new AtomicBoolean();
      *
@@ -6848,8 +7449,8 @@ public final Flowable cache() {
      * 
*
*
Backpressure:
- *
The operator consumes this Publisher in an unbounded fashion but respects the backpressure - * of each downstream Subscriber individually.
+ *
The operator consumes this {@code Publisher} in an unbounded fashion but respects the backpressure + * of each downstream {@code Subscriber} individually.
*
Scheduler:
*
{@code cacheWithInitialCapacity} does not operate by default on a particular {@link Scheduler}.
*
@@ -6858,26 +7459,31 @@ public final Flowable cache() { * {@link #replay(int)} in combination with {@link ConnectableFlowable#autoConnect()} or similar. * * @param initialCapacity hint for number of items to cache (for optimizing underlying data structure) - * @return a Flowable that, when first subscribed to, caches all of its items and notifications for the - * benefit of subsequent subscribers + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code initialCapacity} is non-positive * @see ReactiveX operators documentation: Replay + * @see #takeUntil(Predicate) + * @see #takeUntil(Publisher) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable cacheWithInitialCapacity(int initialCapacity) { ObjectHelper.verifyPositive(initialCapacity, "initialCapacity"); - return RxJavaPlugins.onAssembly(new FlowableCache(this, initialCapacity)); + return RxJavaPlugins.onAssembly(new FlowableCache<>(this, initialCapacity)); } /** - * Returns a Flowable that emits the items emitted by the source Publisher, converted to the specified - * type. + * Returns a {@code Flowable} that emits the upstream items while + * they can be cast via {@link Class#cast(Object)} until the upstream terminates, + * or until the upstream signals an item which can't be cast, + * resulting in a {@link ClassCastException} to be signaled to the downstream. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code cast} does not operate by default on a particular {@link Scheduler}.
@@ -6885,32 +7491,31 @@ public final Flowable cacheWithInitialCapacity(int initialCapacity) { * * @param the output value type cast to * @param clazz - * the target class type that {@code cast} will cast the items emitted by the source Publisher - * into before emitting them from the resulting Publisher - * @return a Flowable that emits each item from the source Publisher after converting it to the - * specified type + * the target class to use to try and cast the upstream items into + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code clazz} is {@code null} * @see ReactiveX operators documentation: Map */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable cast(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + public final <@NonNull U> Flowable cast(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return map(Functions.castFunction(clazz)); } /** - * Collects items emitted by the finite source Publisher into a single mutable data structure and returns - * a Single that emits this structure. + * Collects items emitted by the finite source {@link Publisher} into a single mutable data structure and returns + * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
*
This operator does not support backpressure because by intent it will receive all values and reduce @@ -6925,31 +7530,32 @@ public final Flowable cast(final Class clazz) { * @param collector * a function that accepts the {@code state} and an emitted item, and modifies {@code state} * accordingly - * @return a Single that emits the result of collecting the values emitted by the source Publisher - * into a single mutable data structure + * @return the new {@code Single} instance + * @throws NullPointerException if {@code initialItemSupplier} or {@code collector} is {@code null} * @see ReactiveX operators documentation: Reduce + * @see #collect(Collector) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single collect(Supplier initialItemSupplier, BiConsumer collector) { - ObjectHelper.requireNonNull(initialItemSupplier, "initialItemSupplier is null"); - ObjectHelper.requireNonNull(collector, "collector is null"); - return RxJavaPlugins.onAssembly(new FlowableCollectSingle(this, initialItemSupplier, collector)); + public final <@NonNull U> Single collect(@NonNull Supplier initialItemSupplier, @NonNull BiConsumer collector) { + Objects.requireNonNull(initialItemSupplier, "initialItemSupplier is null"); + Objects.requireNonNull(collector, "collector is null"); + return RxJavaPlugins.onAssembly(new FlowableCollectSingle<>(this, initialItemSupplier, collector)); } /** - * Collects items emitted by the finite source Publisher into a single mutable data structure and returns - * a Single that emits this structure. + * Collects items emitted by the finite source {@link Publisher} into a single mutable data structure and returns + * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
*
This operator does not support backpressure because by intent it will receive all values and reduce @@ -6964,55 +7570,57 @@ public final Single collect(Supplier initialItemSupplier, Bi * @param collector * a function that accepts the {@code state} and an emitted item, and modifies {@code state} * accordingly - * @return a Single that emits the result of collecting the values emitted by the source Publisher - * into a single mutable data structure + * @return the new {@code Single} instance + * @throws NullPointerException if {@code initialItem} or {@code collector} is {@code null} * @see ReactiveX operators documentation: Reduce */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single collectInto(final U initialItem, BiConsumer collector) { - ObjectHelper.requireNonNull(initialItem, "initialItem is null"); + public final <@NonNull U> Single collectInto(U initialItem, @NonNull BiConsumer collector) { + Objects.requireNonNull(initialItem, "initialItem is null"); return collect(Functions.justSupplier(initialItem), collector); } /** - * Transform a Publisher by applying a particular Transformer function to it. + * Transform the current {@code Flowable} by applying a particular {@link FlowableTransformer} function to it. *

- * This method operates on the Publisher itself whereas {@link #lift} operates on the Publisher's - * Subscribers or Subscribers. + * This method operates on the {@code Flowable} itself whereas {@link #lift} operates on the {@code Flowable}'s + * {@link Subscriber}s. *

- * If the operator you are creating is designed to act on the individual items emitted by a source - * Publisher, use {@link #lift}. If your operator is designed to transform the source Publisher as a whole + * If the operator you are creating is designed to act on the individual items emitted by a current + * {@code Flowable}, use {@link #lift}. If your operator is designed to transform the current {@code Flowable} as a whole * (for instance, by applying a particular set of existing RxJava operators to it) use {@code compose}. *

*
Backpressure:
*
The operator itself doesn't interfere with the backpressure behavior which only depends - * on what kind of {@code Publisher} the transformer returns.
+ * on what kind of {@link Publisher} the {@code FlowableTransformer} returns.
*
Scheduler:
*
{@code compose} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the output Publisher - * @param composer implements the function that transforms the source Publisher - * @return the source Publisher, transformed by the transformer function + * @param the value type of the output {@code Publisher} + * @param composer implements the function that transforms the current {@code Flowable} + * @return the new composed {@code Flowable} instance + * @throws NullPointerException if {@code composer} is {@code null} * @see RxJava wiki: Implementing Your Own Operators */ @SuppressWarnings("unchecked") @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable compose(FlowableTransformer composer) { - return fromPublisher(((FlowableTransformer) ObjectHelper.requireNonNull(composer, "composer is null")).apply(this)); + @NonNull + public final <@NonNull R> Flowable compose(@NonNull FlowableTransformer composer) { + return fromPublisher(((FlowableTransformer) Objects.requireNonNull(composer, "composer is null")).apply(this)); } /** - * Returns a new Flowable that emits items resulting from applying a function that you supply to each item - * emitted by the source Publisher, where that function returns a Publisher, and then emitting the items - * that result from concatenating those resulting Publishers. + * Returns a new {@code Flowable} that emits items resulting from applying a function that you supply to each item + * emitted by the current {@code Flowable}, where that function returns a {@link Publisher}, and then emitting the items + * that result from concatenating those returned {@code Publisher}s. *

- * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -7020,35 +7628,36 @@ public final Flowable compose(FlowableTransformer *

*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the inner {@code Publisher}s are - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor - * backpressure, that may throw an {@code IllegalStateException} when that + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor + * backpressure, that may throw an {@link IllegalStateException} when that * {@code Publisher} completes.
*
Scheduler:
*
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the type of the inner Publisher sources and thus the output type + * @param the type of the inner {@code Publisher} sources and thus the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and concatenating the Publishers obtained from this transformation + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMap(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMap(@NonNull Function> mapper) { return concatMap(mapper, 2); } /** - * Returns a new Flowable that emits items resulting from applying a function that you supply to each item - * emitted by the source Publisher, where that function returns a Publisher, and then emitting the items - * that result from concatenating those resulting Publishers. + * Returns a new {@code Flowable} that emits items resulting from applying a function that you supply to each item + * emitted by the current {@code Flowable}, where that function returns a {@link Publisher}, and then emitting the items + * that result from concatenating those returned {@code Publisher}s. *

- * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -7056,22 +7665,23 @@ public final Flowable concatMap(Function *

Backpressure:
*
The operator honors backpressure from downstream. Both this and the inner {@code Publisher}s are - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor - * backpressure, that may throw an {@code IllegalStateException} when that + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor + * backpressure, that may throw an {@link IllegalStateException} when that * {@code Publisher} completes.
*
Scheduler:
*
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the type of the inner Publisher sources and thus the output type + * @param the type of the inner {@code Publisher} sources and thus the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param prefetch - * the number of elements to prefetch from the current Flowable - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and concatenating the Publishers obtained from this transformation + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: FlatMap * @see #concatMap(Function, int, Scheduler) */ @@ -7079,8 +7689,8 @@ public final Flowable concatMap(Function Flowable concatMap(Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMap(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") @@ -7090,39 +7700,40 @@ public final Flowable concatMap(Function(this, mapper, prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new FlowableConcatMap<>(this, mapper, prefetch, ErrorMode.IMMEDIATE)); } /** - * Returns a new Flowable that emits items resulting from applying a function (on a designated scheduler) - * that you supply to each item emitted by the source Publisher, where that function returns a Publisher, and then emitting the items - * that result from concatenating those resulting Publishers. + * Returns a new {@code Flowable} that emits items resulting from applying a function (on a designated scheduler) + * that you supply to each item emitted by the current {@code Flowable}, where that function returns a {@link Publisher}, and then emitting the items + * that result from concatenating those returned {@code Publisher}s. *

- * + * *

* The difference between {@link #concatMap(Function, int)} and this operator is that this operator guarantees the {@code mapper} * function is executed on the specified scheduler. *

*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the inner {@code Publisher}s are - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor - * backpressure, that may throw an {@code IllegalStateException} when that + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor + * backpressure, that may throw an {@link IllegalStateException} when that * {@code Publisher} completes.
*
Scheduler:
*
{@code concatMap} executes the given {@code mapper} function on the provided {@link Scheduler}.
*
* - * @param the type of the inner Publisher sources and thus the output type + * @param the type of the inner {@code Publisher} sources and thus the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param prefetch - * the number of elements to prefetch from the current Flowable + * the number of elements to prefetch from the current {@code Flowable} * @param scheduler * the scheduler where the {@code mapper} function will be executed - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and concatenating the Publishers obtained from this transformation + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 3.0.0 * @see #concatMap(Function, int) @@ -7132,22 +7743,22 @@ public final Flowable concatMap(Function Flowable concatMap(Function> mapper, int prefetch, Scheduler scheduler) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMap(@NonNull Function> mapper, int prefetch, @NonNull Scheduler scheduler) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - ObjectHelper.requireNonNull(scheduler, "scheduler"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapScheduler(this, mapper, prefetch, ErrorMode.IMMEDIATE, scheduler)); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableConcatMapScheduler<>(this, mapper, prefetch, ErrorMode.IMMEDIATE, scheduler)); } /** * Maps the upstream items into {@link CompletableSource}s and subscribes to them one after the * other completes. *

- * + * *

*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
@@ -7155,14 +7766,16 @@ public final Flowable concatMap(Function mapper) { + @NonNull + public final Completable concatMapCompletable(@NonNull Function mapper) { return concatMapCompletable(mapper, 2); } @@ -7170,11 +7783,11 @@ public final Completable concatMapCompletable(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
@@ -7186,7 +7799,9 @@ public final Completable concatMapCompletable(Function mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final Completable concatMapCompletable(@NonNull Function mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapCompletable(this, mapper, ErrorMode.IMMEDIATE, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapCompletable<>(this, mapper, ErrorMode.IMMEDIATE, prefetch)); } /** @@ -7205,11 +7820,11 @@ public final Completable concatMapCompletable(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7217,14 +7832,16 @@ public final Completable concatMapCompletable(Function mapper) { + @NonNull + public final Completable concatMapCompletableDelayError(@NonNull Function mapper) { return concatMapCompletableDelayError(mapper, true, 2); } @@ -7233,11 +7850,11 @@ public final Completable concatMapCompletableDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7251,14 +7868,16 @@ public final Completable concatMapCompletableDelayError(Function mapper, boolean tillTheEnd) { + @NonNull + public final Completable concatMapCompletableDelayError(@NonNull Function mapper, boolean tillTheEnd) { return concatMapCompletableDelayError(mapper, tillTheEnd, 2); } @@ -7267,11 +7886,11 @@ public final Completable concatMapCompletableDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7289,7 +7908,9 @@ public final Completable concatMapCompletableDelayError(Function mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final Completable concatMapCompletableDelayError(@NonNull Function mapper, boolean tillTheEnd, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapCompletable(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapCompletable<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); } /** - * Maps each of the items into a Publisher, subscribes to them one after the other, + * Maps each of the items into a {@link Publisher}, subscribes to them one after the other, * one at a time and emits their values in order - * while delaying any error from either this or any of the inner Publishers + * while delaying any error from either this or any of the inner {@code Publisher}s * till all of them terminate. *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, @@ -7315,30 +7936,32 @@ public final Completable concatMapCompletableDelayError(Function *

Backpressure:
*
The operator honors backpressure from downstream. Both this and the inner {@code Publisher}s are - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor - * backpressure, that may throw an {@code IllegalStateException} when that + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor + * backpressure, that may throw an {@link IllegalStateException} when that * {@code Publisher} completes.
*
Scheduler:
*
{@code concatMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the result value type - * @param mapper the function that maps the items of this Publisher into the inner Publishers. - * @return the new Publisher instance with the concatenation behavior + * @param mapper the function that maps the items of this {@code Publisher} into the inner {@code Publisher}s. + * @return the new {@code Flowable} instance with the concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} * @see #concatMapDelayError(Function, boolean, int, Scheduler) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMapDelayError(@NonNull Function> mapper) { return concatMapDelayError(mapper, true, 2); } /** - * Maps each of the items into a Publisher, subscribes to them one after the other, + * Maps each of the items into a {@link Publisher}, subscribes to them one after the other, * one at a time and emits their values in order - * while delaying any error from either this or any of the inner Publishers + * while delaying any error from either this or any of the inner {@code Publisher}s * till all of them terminate. *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, @@ -7348,31 +7971,33 @@ public final Flowable concatMapDelayError(Function *

Backpressure:
*
The operator honors backpressure from downstream. Both this and the inner {@code Publisher}s are - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor - * backpressure, that may throw an {@code IllegalStateException} when that + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor + * backpressure, that may throw an {@link IllegalStateException} when that * {@code Publisher} completes.
*
Scheduler:
*
{@code concatMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the result value type - * @param mapper the function that maps the items of this Publisher into the inner Publishers. + * @param mapper the function that maps the items of this {@code Publisher} into the inner {@code Publisher}s. * @param tillTheEnd - * if true, all errors from the outer and inner Publisher sources are delayed until the end, - * if false, an error from the main source is signaled when the current Publisher source terminates + * if {@code true}, all errors from the outer and inner {@code Publisher} sources are delayed until the end, + * if {@code false}, an error from the main source is signaled when the current inner {@code Publisher} source terminates * @param prefetch - * the number of elements to prefetch from the current Flowable - * @return the new Publisher instance with the concatenation behavior + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code Flowable} instance with the concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see #concatMapDelayError(Function, boolean, int, Scheduler) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapDelayError(Function> mapper, + public final <@NonNull R> Flowable concatMapDelayError(@NonNull Function> mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") @@ -7382,14 +8007,14 @@ public final Flowable concatMapDelayError(Function(this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); + return RxJavaPlugins.onAssembly(new FlowableConcatMap<>(this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); } /** - * Maps each of the upstream items into a Publisher, subscribes to them one after the other, + * Maps each of the upstream items into a {@link Publisher}, subscribes to them one after the other, * one at a time and emits their values in order * while executing the mapper function on the designated scheduler, delaying any error from either this or any of the - * inner Publishers till all of them terminate. + * inner {@code Publisher}s till all of them terminate. *

* The difference between {@link #concatMapDelayError(Function, boolean, int)} and this operator is that this operator guarantees the {@code mapper} * function is executed on the specified scheduler. @@ -7397,24 +8022,26 @@ public final Flowable concatMapDelayError(Function *

Backpressure:
*
The operator honors backpressure from downstream. Both this and the inner {@code Publisher}s are - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor - * backpressure, that may throw an {@code IllegalStateException} when that + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}. If any of the inner {@code Publisher}s doesn't honor + * backpressure, that may throw an {@link IllegalStateException} when that * {@code Publisher} completes.
*
Scheduler:
*
{@code concatMapDelayError} executes the given {@code mapper} function on the provided {@link Scheduler}.
*
* * @param the result value type - * @param mapper the function that maps the items of this Publisher into the inner Publishers. + * @param mapper the function that maps the items of this {@code Publisher} into the inner {@code Publisher}s. * @param tillTheEnd - * if true, all errors from the outer and inner Publisher sources are delayed until the end, - * if false, an error from the main source is signaled when the current Publisher source terminates + * if {@code true}, all errors from the outer and inner {@code Publisher} sources are delayed until the end, + * if {@code false}, an error from the main source is signaled when the current inner {@code Publisher} source terminates * @param prefetch - * the number of elements to prefetch from the current Flowable + * the number of elements to prefetch from the current {@code Flowable} * @param scheduler * the scheduler where the {@code mapper} function will be executed - * @return the new Publisher instance with the concatenation behavior + * @return the new {@code Flowable} instance with the concatenation behavior + * @throws NullPointerException if {@code mapper} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see #concatMapDelayError(Function, boolean, int) * @since 3.0.0 */ @@ -7422,20 +8049,20 @@ public final Flowable concatMapDelayError(Function Flowable concatMapDelayError(Function> mapper, - boolean tillTheEnd, int prefetch, Scheduler scheduler) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMapDelayError(@NonNull Function> mapper, + boolean tillTheEnd, int prefetch, @NonNull Scheduler scheduler) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapScheduler(this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, scheduler)); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableConcatMapScheduler<>(this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, scheduler)); } /** - * Maps a sequence of values into Publishers and concatenates these Publishers eagerly into a single - * Publisher. + * Maps a sequence of values into {@link Publisher}s and concatenates these {@code Publisher}s eagerly into a single + * {@code Publisher}. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them in + * inner {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them in * order, each one after the previous one completes. *

*
Backpressure:
@@ -7445,24 +8072,26 @@ public final Flowable concatMapDelayError(FunctionThis method does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of Publishers that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code Publisher}s that will be * eagerly concatenated - * @return the new Publisher instance with the specified concatenation behavior + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapEager(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMapEager(@NonNull Function> mapper) { return concatMapEager(mapper, bufferSize(), bufferSize()); } /** - * Maps a sequence of values into Publishers and concatenates these Publishers eagerly into a single - * Publisher. + * Maps a sequence of values into {@link Publisher}s and concatenates these {@code Publisher}s eagerly into a single + * {@code Publisher}. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them in + * inner {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them in * order, each one after the previous one completes. *

*
Backpressure:
@@ -7472,31 +8101,33 @@ public final Flowable concatMapEager(FunctionThis method does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of Publishers that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code Publisher}s that will be * eagerly concatenated - * @param maxConcurrency the maximum number of concurrent subscribed Publishers - * @param prefetch hints about the number of expected values from each inner Publisher, must be positive - * @return the new Publisher instance with the specified concatenation behavior + * @param maxConcurrency the maximum number of concurrent subscribed {@code Publisher}s + * @param prefetch hints about the number of expected values from each inner {@code Publisher}, must be positive + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapEager(Function> mapper, + public final <@NonNull R> Flowable concatMapEager(@NonNull Function> mapper, int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapEager(this, mapper, maxConcurrency, prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapEager<>(this, mapper, maxConcurrency, prefetch, ErrorMode.IMMEDIATE)); } /** - * Maps a sequence of values into Publishers and concatenates these Publishers eagerly into a single - * Publisher. + * Maps a sequence of values into {@link Publisher}s and concatenates these {@code Publisher}s eagerly into a single + * {@code Publisher}. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them in + * inner {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them in * order, each one after the previous one completes. *

*
Backpressure:
@@ -7506,28 +8137,30 @@ public final Flowable concatMapEager(FunctionThis method does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of Publishers that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code Publisher}s that will be * eagerly concatenated * @param tillTheEnd - * if true, all errors from the outer and inner Publisher sources are delayed until the end, - * if false, an error from the main source is signaled when the current Publisher source terminates - * @return the new Publisher instance with the specified concatenation behavior + * if {@code true}, all errors from the outer and inner {@code Publisher} sources are delayed until the end, + * if {@code false}, an error from the main source is signaled when the current inner {@code Publisher} source terminates + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapEagerDelayError(Function> mapper, + @NonNull + public final <@NonNull R> Flowable concatMapEagerDelayError(@NonNull Function> mapper, boolean tillTheEnd) { return concatMapEagerDelayError(mapper, tillTheEnd, bufferSize(), bufferSize()); } /** - * Maps a sequence of values into Publishers and concatenates these Publishers eagerly into a single - * Publisher. + * Maps a sequence of values into {@link Publisher}s and concatenates these {@code Publisher}s eagerly into a single + * {@code Flowable} sequence. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source Publishers. The operator buffers the values emitted by these Publishers and then drains them in + * inner {@code Publisher}s. The operator buffers the values emitted by these {@code Publisher}s and then drains them in * order, each one after the previous one completes. *

*
Backpressure:
@@ -7537,91 +8170,95 @@ public final Flowable concatMapEagerDelayError(FunctionThis method does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of Publishers that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code Publisher}s that will be * eagerly concatenated * @param tillTheEnd - * if true, exceptions from the current Flowable and all the inner Publishers are delayed until - * all of them terminate, if false, exception from the current Flowable is delayed until the - * currently running Publisher terminates - * @param maxConcurrency the maximum number of concurrent subscribed Publishers + * if {@code true}, exceptions from the current {@code Flowable} and all the inner {@code Publisher}s are delayed until + * all of them terminate, if {@code false}, exception from the current {@code Flowable} is delayed until the + * currently running {@code Publisher} terminates + * @param maxConcurrency the maximum number of concurrent subscribed {@code Publisher}s * @param prefetch - * the number of elements to prefetch from each source Publisher - * @return the new Publisher instance with the specified concatenation behavior + * the number of elements to prefetch from each source {@code Publisher} + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapEagerDelayError(Function> mapper, + public final <@NonNull R> Flowable concatMapEagerDelayError(@NonNull Function> mapper, boolean tillTheEnd, int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapEager(this, mapper, maxConcurrency, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapEager<>(this, mapper, maxConcurrency, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); } /** - * Returns a Flowable that concatenate each item emitted by the source Publisher with the values in an - * Iterable corresponding to that item that is generated by a selector. + * Returns a {@code Flowable} that concatenate each item emitted by the current {@code Flowable} with the values in an + * {@link Iterable} corresponding to that item that is generated by a selector. * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ *
The operator honors backpressure from downstream. The current {@code Flowable}s is + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
*
Scheduler:
*
{@code concatMapIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of item emitted by the resulting Publisher + * the type of item emitted by the resulting {@code Flowable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Publisher - * @return a Flowable that emits the results of concatenating the items emitted by the source Publisher with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapIterable(Function> mapper) { + @NonNull + public final <@NonNull U> Flowable concatMapIterable(@NonNull Function> mapper) { return concatMapIterable(mapper, 2); } /** - * Returns a Flowable that concatenate each item emitted by the source Publisher with the values in an - * Iterable corresponding to that item that is generated by a selector. + * Returns a {@code Flowable} that concatenate each item emitted by the current {@code Flowable} with the values in an + * {@link Iterable} corresponding to that item that is generated by a selector. * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ *
The operator honors backpressure from downstream. The current {@code Flowable} is + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
*
Scheduler:
*
{@code concatMapIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of item emitted by the resulting Publisher + * the type of item emitted by the resulting {@code Flowable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Publisher + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Flowable} * @param prefetch - * the number of elements to prefetch from the current Flowable - * @return a Flowable that emits the results of concatenating the items emitted by the source Publisher with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatMapIterable(final Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull U> Flowable concatMapIterable(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableFlattenIterable(this, mapper, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableFlattenIterable<>(this, mapper, prefetch)); } /** @@ -7629,12 +8266,12 @@ public final Flowable concatMapIterable(final Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
*
@@ -7643,7 +8280,8 @@ public final Flowable concatMapIterable(final Function Flowable concatMapIterable(final Function Flowable concatMapMaybe(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMapMaybe(@NonNull Function> mapper) { return concatMapMaybe(mapper, 2); } @@ -7660,12 +8299,12 @@ public final Flowable concatMapMaybe(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
*
@@ -7678,7 +8317,9 @@ public final Flowable concatMapMaybe(Function Flowable concatMapMaybe(Function Flowable concatMapMaybe(Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMapMaybe(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapMaybe(this, mapper, ErrorMode.IMMEDIATE, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapMaybe<>(this, mapper, ErrorMode.IMMEDIATE, prefetch)); } /** @@ -7698,12 +8339,12 @@ public final Flowable concatMapMaybe(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7712,7 +8353,8 @@ public final Flowable concatMapMaybe(Function Flowable concatMapMaybe(Function Flowable concatMapMaybeDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMapMaybeDelayError(@NonNull Function> mapper) { return concatMapMaybeDelayError(mapper, true, 2); } @@ -7729,12 +8372,12 @@ public final Flowable concatMapMaybeDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7749,7 +8392,8 @@ public final Flowable concatMapMaybeDelayError(Function Flowable concatMapMaybeDelayError(Function Flowable concatMapMaybeDelayError(Function> mapper, boolean tillTheEnd) { + @NonNull + public final <@NonNull R> Flowable concatMapMaybeDelayError(@NonNull Function> mapper, boolean tillTheEnd) { return concatMapMaybeDelayError(mapper, tillTheEnd, 2); } @@ -7766,12 +8411,12 @@ public final Flowable concatMapMaybeDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7790,7 +8435,9 @@ public final Flowable concatMapMaybeDelayError(Function Flowable concatMapMaybeDelayError(Function Flowable concatMapMaybeDelayError(Function> mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMapMaybeDelayError(@NonNull Function> mapper, boolean tillTheEnd, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapMaybe(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapMaybe<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); } /** @@ -7809,12 +8456,12 @@ public final Flowable concatMapMaybeDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
*
@@ -7823,7 +8470,8 @@ public final Flowable concatMapMaybeDelayError(Function Flowable concatMapMaybeDelayError(Function Flowable concatMapSingle(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMapSingle(@NonNull Function> mapper) { return concatMapSingle(mapper, 2); } @@ -7840,12 +8489,12 @@ public final Flowable concatMapSingle(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
*
@@ -7858,7 +8507,9 @@ public final Flowable concatMapSingle(Function Flowable concatMapSingle(Function Flowable concatMapSingle(Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMapSingle(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapSingle(this, mapper, ErrorMode.IMMEDIATE, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapSingle<>(this, mapper, ErrorMode.IMMEDIATE, prefetch)); } /** @@ -7878,12 +8529,12 @@ public final Flowable concatMapSingle(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7892,7 +8543,8 @@ public final Flowable concatMapSingle(Function Flowable concatMapSingle(Function Flowable concatMapSingleDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable concatMapSingleDelayError(@NonNull Function> mapper) { return concatMapSingleDelayError(mapper, true, 2); } @@ -7909,12 +8562,12 @@ public final Flowable concatMapSingleDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7929,7 +8582,8 @@ public final Flowable concatMapSingleDelayError(Function Flowable concatMapSingleDelayError(Function Flowable concatMapSingleDelayError(Function> mapper, boolean tillTheEnd) { + @NonNull + public final <@NonNull R> Flowable concatMapSingleDelayError(@NonNull Function> mapper, boolean tillTheEnd) { return concatMapSingleDelayError(mapper, tillTheEnd, 2); } @@ -7946,12 +8601,12 @@ public final Flowable concatMapSingleDelayError(Function - * + * *
*
Backpressure:
*
The operator expects the upstream to support backpressure and honors * the backpressure from downstream. If this {@code Flowable} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ * signal a {@link MissingBackpressureException}. *
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
*
@@ -7970,7 +8625,9 @@ public final Flowable concatMapSingleDelayError(Function Flowable concatMapSingleDelayError(Function Flowable concatMapSingleDelayError(Function> mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable concatMapSingleDelayError(@NonNull Function> mapper, boolean tillTheEnd, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapSingle(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapSingle<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); } /** - * Returns a Flowable that emits the items emitted from the current Publisher, then the next, one after + * Returns a {@code Flowable} that emits the items emitted from the current {@code Flowable}, then the next, one after * the other, without interleaving them. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. Both this and the {@code other} {@code Publisher}s + *
The operator honors backpressure from downstream. Both this and the {@code other} {@link Publisher}s * are expected to honor backpressure as well. If any of then violates this rule, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when the current {@code Flowable} completes. *
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * a Publisher to be concatenated after the current - * @return a Flowable that emits items emitted by the two source Publishers, one after the other, - * without interleaving them + * a {@code Publisher} to be concatenated after the current + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Concat */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatWith(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Flowable concatWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); return concat(this, other); } @@ -8017,7 +8674,7 @@ public final Flowable concatWith(Publisher other) { * Returns a {@code Flowable} that emits the items from this {@code Flowable} followed by the success item or error event * of the other {@link SingleSource}. *

- * + * *

*
Backpressure:
*
The operator supports backpressure and makes sure the success item of the other {@code SingleSource} @@ -8026,23 +8683,25 @@ public final Flowable concatWith(Publisher other) { *
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental - * @param other the SingleSource whose signal should be emitted after this {@code Flowable} completes normally. - * @return the new Flowable instance + * @param other the {@code SingleSource} whose signal should be emitted after this {@code Flowable} completes normally. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable concatWith(@NonNull SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableConcatWithSingle(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableConcatWithSingle<>(this, other)); } /** * Returns a {@code Flowable} that emits the items from this {@code Flowable} followed by the success item or terminal events * of the other {@link MaybeSource}. *

- * + * *

*
Backpressure:
*
The operator supports backpressure and makes sure the success item of the other {@code MaybeSource} @@ -8051,105 +8710,109 @@ public final Flowable concatWith(@NonNull SingleSource other) { *
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental - * @param other the MaybeSource whose signal should be emitted after this Flowable completes normally. - * @return the new Flowable instance + * @param other the {@code MaybeSource} whose signal should be emitted after this {@code Flowable} completes normally. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable concatWith(@NonNull MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableConcatWithMaybe(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableConcatWithMaybe<>(this, other)); } /** * Returns a {@code Flowable} that emits items from this {@code Flowable} and when it completes normally, the * other {@link CompletableSource} is subscribed to and the returned {@code Flowable} emits its terminal events. *

- * + * *

*
Backpressure:
- *
The operator does not interfere with backpressure between the current Flowable and the + *
The operator does not interfere with backpressure between the current {@code Flowable} and the * downstream consumer (i.e., acts as pass-through). When the operator switches to the - * {@code Completable}, backpressure is no longer present because {@code Completable} doesn't + * {@link Completable}, backpressure is no longer present because {@code Completable} doesn't * have items to apply backpressure to.
*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental * @param other the {@code CompletableSource} to subscribe to once the current {@code Flowable} completes normally - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable concatWith(@NonNull CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableConcatWithCompletable(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableConcatWithCompletable<>(this, other)); } /** - * Returns a Single that emits a Boolean that indicates whether the source Publisher emitted a + * Returns a {@link Single} that emits a {@link Boolean} that indicates whether the current {@code Flowable} emitted a * specified item. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code contains} does not operate by default on a particular {@link Scheduler}.
*
* * @param item - * the item to search for in the emissions from the source Publisher - * @return a Flowable that emits {@code true} if the specified item is emitted by the source Publisher, - * or {@code false} if the source Publisher completes without emitting that item + * the item to search for in the emissions from the current {@code Flowable} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single contains(final Object item) { - ObjectHelper.requireNonNull(item, "item is null"); + public final Single contains(@NonNull Object item) { + Objects.requireNonNull(item, "item is null"); return any(Functions.equalsWith(item)); } /** - * Returns a Single that counts the total number of items emitted by the source Publisher and emits - * this count as a 64-bit Long. + * Returns a {@link Single} that counts the total number of items emitted by the current {@code Flowable} and emits + * this count as a 64-bit {@link Long}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code count} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a single item: the number of items emitted by the source Publisher as a - * 64-bit Long item + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Count */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single count() { - return RxJavaPlugins.onAssembly(new FlowableCountSingle(this)); + return RxJavaPlugins.onAssembly(new FlowableCountSingle<>(this)); } /** - * Returns a Flowable that mirrors the source Publisher, except that it drops items emitted by the - * source Publisher that are followed by another item within a computed debounce duration. + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by another item within a computed debounce duration. *

- * + * *

* The delivery of the item happens on the thread of the first {@code onNext} or {@code onComplete} - * signal of the generated {@code Publisher} sequence, + * signal of the generated {@link Publisher} sequence, * which if takes too long, a newer item may arrive from the upstream, causing the * generated sequence to get cancelled, which may also interrupt any downstream blocking operation * (yielding an {@code InterruptedException}). It is recommended processing items @@ -8167,8 +8830,8 @@ public final Single count() { * the debounce value type (ignored) * @param debounceIndicator * function to retrieve a sequence that indicates the throttle duration for each item - * @return a Flowable that omits items emitted by the source Publisher that are followed by another item - * within a computed debounce duration + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code debounceIndicator} is {@code null} * @see ReactiveX operators documentation: Debounce * @see RxJava wiki: Backpressure */ @@ -8176,22 +8839,22 @@ public final Single count() { @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable debounce(Function> debounceIndicator) { - ObjectHelper.requireNonNull(debounceIndicator, "debounceIndicator is null"); - return RxJavaPlugins.onAssembly(new FlowableDebounce(this, debounceIndicator)); + public final <@NonNull U> Flowable debounce(@NonNull Function> debounceIndicator) { + Objects.requireNonNull(debounceIndicator, "debounceIndicator is null"); + return RxJavaPlugins.onAssembly(new FlowableDebounce<>(this, debounceIndicator)); } /** - * Returns a Flowable that mirrors the source Publisher, except that it drops items emitted by the - * source Publisher that are followed by newer items before a timeout value expires. The timer resets on + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by newer items before a timeout value expires. The timer resets on * each emission. *

- * Note: If items keep being emitted by the source Publisher faster than the timeout then no items - * will be emitted by the resulting Publisher. + * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items + * will be emitted by the resulting {@code Flowable}. *

- * + * *

- * Delivery of the item after the grace period happens on the {@code computation} {@code Scheduler}'s + * Delivery of the item after the grace period happens on the {@code computation} {@link Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the * {@code Worker}'s task to get disposed, which may also interrupt any downstream blocking operation * (yielding an {@code InterruptedException}). It is recommended processing items @@ -8201,17 +8864,17 @@ public final Flowable debounce(Function *

Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
{@code debounce} operates by default on the {@code computation} {@link Scheduler}.
+ *
{@code debounce} operates by default on the {@code computation} {@code Scheduler}.
*
* * @param timeout - * the length of the window of time that must pass after the emission of an item from the source - * Publisher in which that Publisher emits no items in order for the item to be emitted by the - * resulting Publisher + * the length of the window of time that must pass after the emission of an item from the current + * {@code Flowable} in which it emits no items in order for the item to be emitted by the + * resulting {@code Flowable} * @param unit * the unit of time for the specified {@code timeout} - * @return a Flowable that filters out items from the source Publisher that are too quickly followed by - * newer items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Debounce * @see RxJava wiki: Backpressure * @see #throttleWithTimeout(long, TimeUnit) @@ -8219,19 +8882,20 @@ public final Flowable debounce(Function @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable debounce(long timeout, TimeUnit unit) { + @NonNull + public final Flowable debounce(long timeout, @NonNull TimeUnit unit) { return debounce(timeout, unit, Schedulers.computation()); } /** - * Returns a Flowable that mirrors the source Publisher, except that it drops items emitted by the - * source Publisher that are followed by newer items before a timeout value expires on a specified - * Scheduler. The timer resets on each emission. + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission. *

- * Note: If items keep being emitted by the source Publisher faster than the timeout then no items - * will be emitted by the resulting Publisher. + * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items + * will be emitted by the resulting {@code Flowable}. *

- * + * *

* Delivery of the item after the grace period happens on the given {@code Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the @@ -8243,19 +8907,19 @@ public final Flowable debounce(long timeout, TimeUnit unit) { *

Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout - * the time each item has to be "the most recent" of those emitted by the source Publisher to + * the time each item has to be "the most recent" of those emitted by the current {@code Flowable} to * ensure that it's not dropped * @param unit * the unit of time for the specified {@code timeout} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle the timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each * item - * @return a Flowable that filters out items from the source Publisher that are too quickly followed by - * newer items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Debounce * @see RxJava wiki: Backpressure * @see #throttleWithTimeout(long, TimeUnit, Scheduler) @@ -8264,53 +8928,105 @@ public final Flowable debounce(long timeout, TimeUnit unit) { @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableDebounceTimed(this, timeout, unit, scheduler)); + public final Flowable debounce(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableDebounceTimed<>(this, timeout, unit, scheduler, null)); + } + + /** + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission. + *

+ * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items + * will be emitted by the resulting {@code Flowable}. + *

+ * + *

+ * Delivery of the item after the grace period happens on the given {@code Scheduler}'s + * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the + * {@code Worker}'s task to get disposed, which may also interrupt any downstream blocking operation + * (yielding an {@code InterruptedException}). It is recommended processing items + * that may take long time to be moved to another thread via {@link #observeOn} applied after + * {@code debounce} itself. + *

+ *
Backpressure:
+ *
This operator does not support backpressure as it uses time to control data flow.
+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param timeout + * the time each item has to be "the most recent" of those emitted by the current {@code Flowable} to + * ensure that it's not dropped + * @param unit + * the unit of time for the specified {@code timeout} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each + * item + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Debounce + * @see RxJava wiki: Backpressure + * @see #throttleWithTimeout(long, TimeUnit, Scheduler, Consumer) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.ERROR) + @SchedulerSupport(SchedulerSupport.CUSTOM) + @Experimental + public final Flowable debounce(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new FlowableDebounceTimed<>(this, timeout, unit, scheduler, onDropped)); } /** - * Returns a Flowable that emits the items emitted by the source Publisher or a specified default item - * if the source Publisher is empty. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} or a specified default item + * if the current {@code Flowable} is empty. *

- * + * *

*
Backpressure:
- *
If the source {@code Publisher} is empty, this operator is guaranteed to honor backpressure from downstream. - * If the source {@code Publisher} is non-empty, it is expected to honor backpressure as well; if the rule is violated, - * a {@code MissingBackpressureException} may get signaled somewhere downstream. + *
If the current {@code Flowable} is empty, this operator is guaranteed to honor backpressure from downstream. + * If the current {@code Flowable} is non-empty, it is expected to honor backpressure as well; if the rule is violated, + * a {@link MissingBackpressureException} may get signaled somewhere downstream. *
*
Scheduler:
*
{@code defaultIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the item to emit if the source Publisher emits no items - * @return a Flowable that emits either the specified default item if the source Publisher emits no - * items, or the items emitted by the source Publisher + * the item to emit if the current {@code Flowable} emits no items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: DefaultIfEmpty */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable defaultIfEmpty(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); + public final Flowable defaultIfEmpty(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); return switchIfEmpty(just(defaultItem)); } /** - * Returns a Flowable that delays the emissions of the source Publisher via another Publisher on a + * Returns a {@code Flowable} that delays the emissions of the current {@code Flowable} via another {@link Publisher} on a * per-item basis. *

- * + * *

- * Note: the resulting Publisher will immediately propagate any {@code onError} notification - * from the source Publisher. + * Note: the resulting {@code Flowable} will immediately propagate any {@code onError} notification + * from the current {@code Flowable}. *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}. + *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}. * All of the other {@code Publisher}s supplied by the function are consumed * in an unbounded manner (i.e., no backpressure applied to them).
*
Scheduler:
@@ -8320,151 +9036,158 @@ public final Flowable defaultIfEmpty(T defaultItem) { * @param * the item delay value type (ignored) * @param itemDelayIndicator - * a function that returns a Publisher for each item emitted by the source Publisher, which is - * then used to delay the emission of that item by the resulting Publisher until the Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable}, which is + * then used to delay the emission of that item by the resulting {@code Flowable} until the {@code Publisher} * returned from {@code itemDelay} emits an item - * @return a Flowable that delays the emissions of the source Publisher via another Publisher on a - * per-item basis + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code itemDelayIndicator} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable delay(final Function> itemDelayIndicator) { - ObjectHelper.requireNonNull(itemDelayIndicator, "itemDelayIndicator is null"); + public final <@NonNull U> Flowable delay(@NonNull Function> itemDelayIndicator) { + Objects.requireNonNull(itemDelayIndicator, "itemDelayIndicator is null"); return flatMap(FlowableInternalHelper.itemDelay(itemDelayIndicator)); } /** - * Returns a Flowable that emits the items emitted by the source Publisher shifted forward in time by a - * specified delay. Error notifications from the source Publisher are not delayed. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a + * specified delay. The {@code onError} notification from the current {@code Flowable} is not delayed. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}.
+ *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the {@link TimeUnit} in which {@code period} is defined - * @return the source Publisher shifted in time by the specified delay + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable delay(long delay, TimeUnit unit) { - return delay(delay, unit, Schedulers.computation(), false); + @NonNull + public final Flowable delay(long time, @NonNull TimeUnit unit) { + return delay(time, unit, Schedulers.computation(), false); } /** - * Returns a Flowable that emits the items emitted by the source Publisher shifted forward in time by a - * specified delay. If {@code delayError} is true, error notifications will also be delayed. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a + * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}.
+ *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param delayError - * if true, the upstream exception is signaled with the given delay, after all preceding normal elements, - * if false, the upstream exception is signaled immediately - * @return the source Publisher shifted in time by the specified delay + * if {@code true}, the upstream exception is signaled with the given delay, after all preceding normal elements, + * if {@code false}, the upstream exception is signaled immediately + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable delay(long delay, TimeUnit unit, boolean delayError) { - return delay(delay, unit, Schedulers.computation(), delayError); + @NonNull + public final Flowable delay(long time, @NonNull TimeUnit unit, boolean delayError) { + return delay(time, unit, Schedulers.computation(), delayError); } /** - * Returns a Flowable that emits the items emitted by the source Publisher shifted forward in time by a - * specified delay. Error notifications from the source Publisher are not delayed. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a + * specified delay. The {@code onError} notification from the current {@code Flowable} is not delayed. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}.
+ *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the time unit of {@code delay} * @param scheduler - * the {@link Scheduler} to use for delaying - * @return the source Publisher shifted in time by the specified delay + * the {@code Scheduler} to use for delaying + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable delay(long delay, TimeUnit unit, Scheduler scheduler) { - return delay(delay, unit, scheduler, false); + @NonNull + public final Flowable delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delay(time, unit, scheduler, false); } /** - * Returns a Flowable that emits the items emitted by the source Publisher shifted forward in time by a - * specified delay. If {@code delayError} is true, error notifications will also be delayed. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} shifted forward in time by a + * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}.
+ *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the time unit of {@code delay} * @param scheduler - * the {@link Scheduler} to use for delaying + * the {@code Scheduler} to use for delaying * @param delayError - * if true, the upstream exception is signaled with the given delay, after all preceding normal elements, - * if false, the upstream exception is signaled immediately - * @return the source Publisher shifted in time by the specified delay + * if {@code true}, the upstream exception is signaled with the given delay, after all preceding normal elements, + * if {@code false}, the upstream exception is signaled immediately + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable delay(long delay, TimeUnit unit, Scheduler scheduler, boolean delayError) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Flowable delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableDelay(this, Math.max(0L, delay), unit, scheduler, delayError)); + return RxJavaPlugins.onAssembly(new FlowableDelay<>(this, Math.max(0L, time), unit, scheduler, delayError)); } /** - * Returns a Flowable that delays the subscription to and emissions from the source Publisher via another - * Publisher on a per-item basis. + * Returns a {@code Flowable} that delays the subscription to and emissions from the current {@code Flowable} via another + * {@link Publisher} on a per-item basis. *

- * + * *

- * Note: the resulting Publisher will immediately propagate any {@code onError} notification - * from the source Publisher. + * Note: the resulting {@code Flowable} will immediately propagate any {@code onError} notification + * from the current {@code Flowable}. *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}. + *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}. * All of the other {@code Publisher}s supplied by the functions are consumed * in an unbounded manner (i.e., no backpressure applied to them).
*
Scheduler:
@@ -8476,111 +9199,115 @@ public final Flowable delay(long delay, TimeUnit unit, Scheduler scheduler, b * @param * the item delay value type (ignored) * @param subscriptionIndicator - * a function that returns a Publisher that triggers the subscription to the source Publisher + * a function that returns a {@code Publisher} that triggers the subscription to the current {@code Flowable} * once it emits any item * @param itemDelayIndicator - * a function that returns a Publisher for each item emitted by the source Publisher, which is - * then used to delay the emission of that item by the resulting Publisher until the Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable}, which is + * then used to delay the emission of that item by the resulting {@code Flowable} until the {@code Publisher} * returned from {@code itemDelay} emits an item - * @return a Flowable that delays the subscription and emissions of the source Publisher via another - * Publisher on a per-item basis + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code subscriptionIndicator} and {@code itemDelayIndicator} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable delay(Publisher subscriptionIndicator, - Function> itemDelayIndicator) { + @NonNull + public final <@NonNull U, @NonNull V> Flowable delay(@NonNull Publisher subscriptionIndicator, + @NonNull Function> itemDelayIndicator) { return delaySubscription(subscriptionIndicator).delay(itemDelayIndicator); } /** - * Returns a Flowable that delays the subscription to this Publisher - * until the other Publisher emits an element or completes normally. + * Returns a {@code Flowable} that delays the subscription to this {@link Publisher} + * until the other {@code Publisher} emits an element or completes normally. *
*
Backpressure:
- *
The operator forwards the backpressure requests to this Publisher once - * the subscription happens and requests Long.MAX_VALUE from the other Publisher
+ *
The operator forwards the backpressure requests to this {@code Publisher} once + * the subscription happens and requests {@link Long#MAX_VALUE} from the other {@code Publisher}
*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the other Publisher, irrelevant - * @param subscriptionIndicator the other Publisher that should trigger the subscription - * to this Publisher. - * @return a Flowable that delays the subscription to this Publisher - * until the other Publisher emits an element or completes normally. + * @param the value type of the other {@code Publisher}, irrelevant + * @param subscriptionIndicator the other {@code Publisher} that should trigger the subscription + * to this {@code Publisher}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable delaySubscription(Publisher subscriptionIndicator) { - ObjectHelper.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); - return RxJavaPlugins.onAssembly(new FlowableDelaySubscriptionOther(this, subscriptionIndicator)); + public final <@NonNull U> Flowable delaySubscription(@NonNull Publisher subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new FlowableDelaySubscriptionOther<>(this, subscriptionIndicator)); } /** - * Returns a Flowable that delays the subscription to the source Publisher by a given amount of time. + * Returns a {@code Flowable} that delays the subscription to the current {@code Flowable} by a given amount of time. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}.
+ *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
*
Scheduler:
*
This version of {@code delaySubscription} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the time to delay the subscription * @param unit * the time unit of {@code delay} - * @return a Flowable that delays the subscription to the source Publisher by the given amount + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable delaySubscription(long delay, TimeUnit unit) { - return delaySubscription(delay, unit, Schedulers.computation()); + @NonNull + public final Flowable delaySubscription(long time, @NonNull TimeUnit unit) { + return delaySubscription(time, unit, Schedulers.computation()); } /** - * Returns a Flowable that delays the subscription to the source Publisher by a given amount of time, - * both waiting and subscribing on a given Scheduler. + * Returns a {@code Flowable} that delays the subscription to the current {@code Flowable} by a given amount of time, + * both waiting and subscribing on a given {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with the backpressure behavior which is determined by the source {@code Publisher}.
+ *
The operator doesn't interfere with the backpressure behavior which is determined by the current {@code Flowable}.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* - * @param delay + * @param time * the time to delay the subscription * @param unit * the time unit of {@code delay} * @param scheduler - * the Scheduler on which the waiting and subscription will happen - * @return a Flowable that delays the subscription to the source Publisher by a given - * amount, waiting and subscribing on the given Scheduler + * the {@code Scheduler} on which the waiting and subscription will happen + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable delaySubscription(long delay, TimeUnit unit, Scheduler scheduler) { - return delaySubscription(timer(delay, unit, scheduler)); + @NonNull + public final Flowable delaySubscription(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delaySubscription(timer(time, unit, scheduler)); } /** - * Returns a Flowable that reverses the effect of {@link #materialize materialize} by transforming the + * Returns a {@code Flowable} that reverses the effect of {@link #materialize materialize} by transforming the * {@link Notification} objects extracted from the source items via a selector function - * into their respective {@code Subscriber} signal types. + * into their respective {@link Subscriber} signal types. *

- * + * *

* The intended use of the {@code selector} function is to perform a * type-safe identity mapping (see example) on a source that is already of type @@ -8590,7 +9317,7 @@ public final Flowable delaySubscription(long delay, TimeUnit unit, Scheduler *

* When the upstream signals an {@link Notification#createOnError(Throwable) onError} or * {@link Notification#createOnComplete() onComplete} item, the - * returned Flowable cancels of the flow and terminates with that type of terminal event: + * returned {@code Flowable} cancels of the flow and terminates with that type of terminal event: *


      * Flowable.just(createOnNext(1), createOnComplete(), createOnNext(2))
      * .doOnCancel(() -> System.out.println("Canceled!"));
@@ -8610,7 +9337,7 @@ public final Flowable delaySubscription(long delay, TimeUnit unit, Scheduler
      * with a {@link #never()} source.
      * 
*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code dematerialize} does not operate by default on a particular {@link Scheduler}.
@@ -8618,10 +9345,10 @@ public final Flowable delaySubscription(long delay, TimeUnit unit, Scheduler *

History: 2.2.4 - experimental * * @param the output value type - * @param selector function that returns the upstream item and should return a Notification to signal + * @param selector function that returns the upstream item and should return a {@code Notification} to signal * the corresponding {@code Subscriber} event to the downstream. - * @return a Flowable that emits the items and notifications embedded in the {@link Notification} objects - * selected from the items emitted by the source Flowable + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} is {@code null} * @see ReactiveX operators documentation: Dematerialize * @since 3.0.0 */ @@ -8629,40 +9356,39 @@ public final Flowable delaySubscription(long delay, TimeUnit unit, Scheduler @NonNull @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.PASS_THROUGH) - public final Flowable dematerialize(Function> selector) { - ObjectHelper.requireNonNull(selector, "selector is null"); - return RxJavaPlugins.onAssembly(new FlowableDematerialize(this, selector)); + public final <@NonNull R> Flowable dematerialize(@NonNull Function<@NonNull ? super T, @NonNull Notification> selector) { + Objects.requireNonNull(selector, "selector is null"); + return RxJavaPlugins.onAssembly(new FlowableDematerialize<>(this, selector)); } /** - * Returns a Flowable that emits all items emitted by the source Publisher that are distinct + * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct * based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} and {@link Object#hashCode()} to provide * a meaningful comparison between items as the default Java implementation only considers reference equivalence. *

- * By default, {@code distinct()} uses an internal {@link java.util.HashSet} per Subscriber to remember + * By default, {@code distinct()} uses an internal {@link java.util.HashSet} per {@link Subscriber} to remember * previously seen items and uses {@link java.util.Set#add(Object)} returning {@code false} as the * indicator for duplicates. *

- * Note that this internal {@code HashSet} may grow unbounded as items won't be removed from it by + * Note that this internal {@link HashSet} may grow unbounded as items won't be removed from it by * the operator. Therefore, using very long or infinite upstream (with very distinct elements) may lead - * to {@code OutOfMemoryError}. + * to {@link OutOfMemoryError}. *

* Customizing the retention policy can happen only by providing a custom {@link java.util.Collection} implementation * to the {@link #distinct(Function, Supplier)} overload. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code distinct} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits only those items emitted by the source Publisher that are distinct from - * each other + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Distinct * @see #distinct(Function) * @see #distinct(Function, Supplier) @@ -8671,33 +9397,34 @@ public final Flowable dematerialize(Function> @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable distinct() { return distinct((Function)Functions.identity(), Functions.createHashSet()); } /** - * Returns a Flowable that emits all items emitted by the source Publisher that are distinct according + * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct according * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} and {@link Object#hashCode()} to provide * a meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. *

- * By default, {@code distinct()} uses an internal {@link java.util.HashSet} per Subscriber to remember + * By default, {@code distinct()} uses an internal {@link java.util.HashSet} per {@link Subscriber} to remember * previously seen keys and uses {@link java.util.Set#add(Object)} returning {@code false} as the * indicator for duplicates. *

- * Note that this internal {@code HashSet} may grow unbounded as keys won't be removed from it by + * Note that this internal {@link HashSet} may grow unbounded as keys won't be removed from it by * the operator. Therefore, using very long or infinite upstream (with very distinct keys) may lead - * to {@code OutOfMemoryError}. + * to {@link OutOfMemoryError}. *

* Customizing the retention policy can happen only by providing a custom {@link java.util.Collection} implementation * to the {@link #distinct(Function, Supplier)} overload. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code distinct} does not operate by default on a particular {@link Scheduler}.
@@ -8707,29 +9434,31 @@ public final Flowable distinct() { * @param keySelector * a function that projects an emitted item to a key value that is used to decide whether an item * is distinct from another one or not - * @return a Flowable that emits those items emitted by the source Publisher that have distinct keys + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: Distinct * @see #distinct(Function, Supplier) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable distinct(Function keySelector) { - return distinct(keySelector, Functions.createHashSet()); + @NonNull + public final <@NonNull K> Flowable distinct(@NonNull Function keySelector) { + return distinct(keySelector, Functions.createHashSet()); } /** - * Returns a Flowable that emits all items emitted by the source Publisher that are distinct according + * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct according * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} and {@link Object#hashCode()} to provide * a meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code distinct} does not operate by default on a particular {@link Scheduler}.
@@ -8740,26 +9469,28 @@ public final Flowable distinct(Function keySelector) { * a function that projects an emitted item to a key value that is used to decide whether an item * is distinct from another one or not * @param collectionSupplier - * function called for each individual Subscriber to return a Collection subtype for holding the extracted + * function called for each individual {@link Subscriber} to return a {@link Collection} subtype for holding the extracted * keys and whose add() method's return indicates uniqueness. - * @return a Flowable that emits those items emitted by the source Publisher that have distinct keys + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} or {@code collectionSupplier} is {@code null} * @see ReactiveX operators documentation: Distinct */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable distinct(Function keySelector, - Supplier> collectionSupplier) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(collectionSupplier, "collectionSupplier is null"); - return RxJavaPlugins.onAssembly(new FlowableDistinct(this, keySelector, collectionSupplier)); + @NonNull + public final <@NonNull K> Flowable distinct(@NonNull Function keySelector, + @NonNull Supplier> collectionSupplier) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(collectionSupplier, "collectionSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableDistinct<>(this, keySelector, collectionSupplier)); } /** - * Returns a Flowable that emits all items emitted by the source Publisher that are distinct from their + * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct from their * immediate predecessors based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} to provide * a meaningful comparison between items as the default Java implementation only considers reference equivalence. @@ -8772,36 +9503,36 @@ public final Flowable distinct(Function keySelector, *

* Note that if element type {@code T} in the flow is mutable, the comparison of the previous and current * item may yield unexpected results if the items are mutated externally. Common cases are mutable - * {@code CharSequence}s or {@code List}s where the objects will actually have the same + * {@link CharSequence}s or {@link List}s where the objects will actually have the same * references when they are modified and {@code distinctUntilChanged} will evaluate subsequent items as same. * To avoid such situation, it is recommended that mutable data is converted to an immutable one, * for example using {@code map(CharSequence::toString)} or {@code map(list -> Collections.unmodifiableList(new ArrayList<>(list)))}. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code distinctUntilChanged} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits those items from the source Publisher that are distinct from their - * immediate predecessors + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Distinct * @see #distinctUntilChanged(BiPredicate) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable distinctUntilChanged() { return distinctUntilChanged(Functions.identity()); } /** - * Returns a Flowable that emits all items emitted by the source Publisher that are distinct from their + * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct from their * immediate predecessors, according to a key selector function and based on {@link Object#equals(Object)} comparison * of those objects returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} to provide * a meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. @@ -8815,13 +9546,13 @@ public final Flowable distinctUntilChanged() { *

* Note that if element type {@code T} in the flow is mutable, the comparison of the previous and current * item may yield unexpected results if the items are mutated externally. Common cases are mutable - * {@code CharSequence}s or {@code List}s where the objects will actually have the same + * {@link CharSequence}s or {@link List}s where the objects will actually have the same * references when they are modified and {@code distinctUntilChanged} will evaluate subsequent items as same. * To avoid such situation, it is recommended that mutable data is converted to an immutable one, * for example using {@code map(CharSequence::toString)} or {@code map(list -> Collections.unmodifiableList(new ArrayList<>(list)))}. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code distinctUntilChanged} does not operate by default on a particular {@link Scheduler}.
@@ -8831,58 +9562,60 @@ public final Flowable distinctUntilChanged() { * @param keySelector * a function that projects an emitted item to a key value that is used to decide whether an item * is distinct from another one or not - * @return a Flowable that emits those items from the source Publisher whose keys are distinct from - * those of their immediate predecessors + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: Distinct */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable distinctUntilChanged(Function keySelector) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - return RxJavaPlugins.onAssembly(new FlowableDistinctUntilChanged(this, keySelector, ObjectHelper.equalsPredicate())); + @NonNull + public final <@NonNull K> Flowable distinctUntilChanged(@NonNull Function keySelector) { + Objects.requireNonNull(keySelector, "keySelector is null"); + return RxJavaPlugins.onAssembly(new FlowableDistinctUntilChanged<>(this, keySelector, ObjectHelper.equalsPredicate())); } /** - * Returns a Flowable that emits all items emitted by the source Publisher that are distinct from their + * Returns a {@code Flowable} that emits all items emitted by the current {@code Flowable} that are distinct from their * immediate predecessors when compared with each other via the provided comparator function. *

- * + * *

* Note that the operator always retains the latest item from upstream regardless of the comparison result * and uses it in the next comparison with the next upstream item. *

* Note that if element type {@code T} in the flow is mutable, the comparison of the previous and current * item may yield unexpected results if the items are mutated externally. Common cases are mutable - * {@code CharSequence}s or {@code List}s where the objects will actually have the same + * {@link CharSequence}s or {@link List}s where the objects will actually have the same * references when they are modified and {@code distinctUntilChanged} will evaluate subsequent items as same. * To avoid such situation, it is recommended that mutable data is converted to an immutable one, * for example using {@code map(CharSequence::toString)} or {@code map(list -> Collections.unmodifiableList(new ArrayList<>(list)))}. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code distinctUntilChanged} does not operate by default on a particular {@link Scheduler}.
*
* * @param comparer the function that receives the previous item and the current item and is - * expected to return true if the two are equal, thus skipping the current value. - * @return a Flowable that emits those items from the source Publisher that are distinct from their - * immediate predecessors + * expected to return {@code true} if the two are equal, thus skipping the current value. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code comparer} is {@code null} * @see ReactiveX operators documentation: Distinct * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable distinctUntilChanged(BiPredicate comparer) { - ObjectHelper.requireNonNull(comparer, "comparer is null"); - return RxJavaPlugins.onAssembly(new FlowableDistinctUntilChanged(this, Functions.identity(), comparer)); + @NonNull + public final Flowable distinctUntilChanged(@NonNull BiPredicate comparer) { + Objects.requireNonNull(comparer, "comparer is null"); + return RxJavaPlugins.onAssembly(new FlowableDistinctUntilChanged<>(this, Functions.identity(), comparer)); } /** - * Calls the specified action after this Flowable signals onError or onCompleted or gets canceled by + * Calls the specified action after this {@code Flowable} signals {@code onError} or {@code onComplete} or gets canceled by * the downstream. *

In case of a race between a terminal event and a cancellation, the provided {@code onFinally} action * is executed once per subscription. @@ -8890,25 +9623,27 @@ public final Flowable distinctUntilChanged(BiPredicate * should be thread-safe. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code doFinally} does not operate by default on a particular {@link Scheduler}.
*
Operator-fusion:
- *
This operator supports normal and conditional Subscribers as well as boundary-limited + *
This operator supports normal and conditional {@link Subscriber}s as well as boundary-limited * synchronous or asynchronous queue-fusion.
*
*

History: 2.0.1 - experimental - * @param onFinally the action called when this Flowable terminates or gets canceled - * @return the new Flowable instance + * @param onFinally the action called when this {@code Flowable} terminates or gets canceled + * @throws NullPointerException if {@code onFinally} is {@code null} + * @return the new {@code Flowable} instance * @since 2.1 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doFinally(Action onFinally) { - ObjectHelper.requireNonNull(onFinally, "onFinally is null"); - return RxJavaPlugins.onAssembly(new FlowableDoFinally(this, onFinally)); + @NonNull + public final Flowable doFinally(@NonNull Action onFinally) { + Objects.requireNonNull(onFinally, "onFinally is null"); + return RxJavaPlugins.onAssembly(new FlowableDoFinally<>(this, onFinally)); } /** @@ -8917,67 +9652,68 @@ public final Flowable doFinally(Action onFinally) { * should be thread-safe. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code doAfterNext} does not operate by default on a particular {@link Scheduler}.
*
Operator-fusion:
- *
This operator supports normal and conditional Subscribers as well as boundary-limited + *
This operator supports normal and conditional {@link Subscriber}s as well as boundary-limited * synchronous or asynchronous queue-fusion.
*
*

History: 2.0.1 - experimental - * @param onAfterNext the Consumer that will be called after emitting an item from upstream to the downstream - * @return the new Flowable instance + * @param onAfterNext the {@link Consumer} that will be called after emitting an item from upstream to the downstream + * @throws NullPointerException if {@code onAfterNext} is {@code null} + * @return the new {@code Flowable} instance * @since 2.1 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doAfterNext(Consumer onAfterNext) { - ObjectHelper.requireNonNull(onAfterNext, "onAfterNext is null"); - return RxJavaPlugins.onAssembly(new FlowableDoAfterNext(this, onAfterNext)); + @NonNull + public final Flowable doAfterNext(@NonNull Consumer onAfterNext) { + Objects.requireNonNull(onAfterNext, "onAfterNext is null"); + return RxJavaPlugins.onAssembly(new FlowableDoAfterNext<>(this, onAfterNext)); } /** - * Registers an {@link Action} to be called when this Publisher invokes either + * Registers an {@link Action} to be called when this {@link Publisher} invokes either * {@link Subscriber#onComplete onComplete} or {@link Subscriber#onError onError}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code doAfterTerminate} does not operate by default on a particular {@link Scheduler}.
*
* * @param onAfterTerminate - * an {@link Action} to be invoked when the source Publisher finishes - * @return a Flowable that emits the same items as the source Publisher, then invokes the - * {@link Action} + * an {@code Action} to be invoked when the current {@code Flowable} finishes + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @see #doOnTerminate(Action) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doAfterTerminate(Action onAfterTerminate) { + @NonNull + public final Flowable doAfterTerminate(@NonNull Action onAfterTerminate) { return doOnEach(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, onAfterTerminate); } /** - * Calls the cancel {@code Action} if the downstream cancels the sequence. + * Calls the cancel {@link Action} if the downstream cancels the sequence. + *

+ * *

* The action is shared between subscriptions and thus may be called concurrently from multiple * threads; the action must be thread-safe. *

* If the action throws a runtime exception, that exception is rethrown by the {@code onCancel()} call, - * sometimes as a {@code CompositeException} if there were multiple exceptions along the way. - *

- * Note that terminal events trigger the action unless the {@code Publisher} is subscribed to via {@code unsafeSubscribe()}. - *

- * + * sometimes as a {@link CompositeException} if there were multiple exceptions along the way. *

*
Backpressure:
*
{@code doOnCancel} does not interact with backpressure requests or value delivery; backpressure @@ -8987,38 +9723,42 @@ public final Flowable doAfterTerminate(Action onAfterTerminate) { *
* * @param onCancel - * the action that gets called when the source {@code Publisher}'s Subscription is canceled - * @return the source {@code Publisher} modified so as to call this Action when appropriate + * the action that gets called when the current {@code Flowable}'s {@link Subscription} is canceled + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onCancel} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnCancel(Action onCancel) { + @NonNull + public final Flowable doOnCancel(@NonNull Action onCancel) { return doOnLifecycle(Functions.emptyConsumer(), Functions.EMPTY_LONG_CONSUMER, onCancel); } /** - * Modifies the source Publisher so that it invokes an action when it calls {@code onComplete}. + * Invokes an {@link Action} just before the current {@code Flowable} calls {@code onComplete}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnComplete} does not operate by default on a particular {@link Scheduler}.
*
* * @param onComplete - * the action to invoke when the source Publisher calls {@code onComplete} - * @return the source Publisher with the side-effecting behavior applied + * the action to invoke when the current {@code Flowable} calls {@code onComplete} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnComplete(Action onComplete) { + @NonNull + public final Flowable doOnComplete(@NonNull Action onComplete) { return doOnEach(Functions.emptyConsumer(), Functions.emptyConsumer(), onComplete, Functions.EMPTY_ACTION); } @@ -9027,54 +9767,61 @@ public final Flowable doOnComplete(Action onComplete) { * Calls the appropriate onXXX consumer (shared between all subscribers) whenever a signal with the same type * passes through, before forwarding them to downstream. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source Publisher with the side-effecting behavior applied + * @param onNext the {@link Consumer} to invoke when the current {@code Flowable} calls {@code onNext} + * @param onError the {@code Consumer} to invoke when the current {@code Flowable} calls {@code onError} + * @param onComplete the {@link Action} to invoke when the current {@code Flowable} calls {@code onComplete} + * @param onAfterTerminate the {@code Action} to invoke when the current {@code Flowable} calls {@code onAfterTerminate} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onNext}, {@code onError}, {@code onComplete} or {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - private Flowable doOnEach(Consumer onNext, Consumer onError, + private Flowable doOnEach(@NonNull Consumer onNext, @NonNull Consumer onError, Action onComplete, Action onAfterTerminate) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); - return RxJavaPlugins.onAssembly(new FlowableDoOnEach(this, onNext, onError, onComplete, onAfterTerminate)); + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); + return RxJavaPlugins.onAssembly(new FlowableDoOnEach<>(this, onNext, onError, onComplete, onAfterTerminate)); } /** - * Modifies the source Publisher so that it invokes an action for each item it emits. + * Invokes a {@link Consumer} with a {@link Notification} instances matching the signals emitted by the current {@code Flowable} + * before they are forwarded to the downstream. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNotification - * the action to invoke for each item emitted by the source Publisher - * @return the source Publisher with the side-effecting behavior applied + * the action to invoke for each item emitted by the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onNotification} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnEach(final Consumer> onNotification) { - ObjectHelper.requireNonNull(onNotification, "onNotification is null"); + public final Flowable doOnEach(@NonNull Consumer<@NonNull ? super Notification> onNotification) { + Objects.requireNonNull(onNotification, "onNotification is null"); return doOnEach( Functions.notificationOnNext(onNotification), Functions.notificationOnError(onNotification), @@ -9084,34 +9831,36 @@ public final Flowable doOnEach(final Consumer> onNoti } /** - * Modifies the source Publisher so that it notifies a Subscriber for each item and terminal event it emits. + * Calls the appropriate methods of the given {@link Subscriber} when the current {@code Flowable} signals events before forwarding it + * to the downstream. *

- * In case the {@code onError} of the supplied Subscriber throws, the downstream will receive a composite + * In case the {@code onError} of the supplied {@code Subscriber} throws, the downstream will receive a composite * exception containing the original exception and the exception thrown by {@code onError}. If either the - * {@code onNext} or the {@code onComplete} method of the supplied Subscriber throws, the downstream will be + * {@code onNext} or the {@code onComplete} method of the supplied {@code Subscriber} throws, the downstream will be * terminated and will receive this thrown exception. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
*
* * @param subscriber - * the Subscriber to be notified about onNext, onError and onComplete events on its - * respective methods before the actual downstream Subscriber gets notified. - * @return the source Publisher with the side-effecting behavior applied + * the {@code Subscriber} to be notified about {@code onNext}, {@code onError} and {@code onComplete} events on its + * respective methods before the actual downstream {@code Subscriber} gets notified. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code subscriber} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnEach(final Subscriber subscriber) { - ObjectHelper.requireNonNull(subscriber, "subscriber is null"); + public final Flowable doOnEach(@NonNull Subscriber subscriber) { + Objects.requireNonNull(subscriber, "subscriber is null"); return doOnEach( FlowableInternalHelper.subscriberOnNext(subscriber), FlowableInternalHelper.subscriberOnError(subscriber), @@ -9120,110 +9869,118 @@ public final Flowable doOnEach(final Subscriber subscriber) { } /** - * Modifies the source Publisher so that it invokes an action if it calls {@code onError}. + * Calls the given {@link Consumer} with the error {@link Throwable} if the current {@code Flowable} failed before forwarding it to + * the downstream. *

* In case the {@code onError} action throws, the downstream will receive a composite exception containing * the original exception and the exception thrown by {@code onError}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
*
* * @param onError - * the action to invoke if the source Publisher calls {@code onError} - * @return the source Publisher with the side-effecting behavior applied + * the action to invoke if the current {@code Flowable} calls {@code onError} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onError} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnError(Consumer onError) { + @NonNull + public final Flowable doOnError(@NonNull Consumer onError) { return doOnEach(Functions.emptyConsumer(), onError, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Calls the appropriate onXXX method (shared between all Subscribers) for the lifecycle events of + * Calls the appropriate {@code onXXX} method (shared between all {@link Subscriber}s) for the lifecycle events of * the sequence (subscription, cancellation, requesting). *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
*
* * @param onSubscribe - * a Consumer called with the Subscription sent via Subscriber.onSubscribe() + * a {@link Consumer} called with the {@link Subscription} sent via {@link Subscriber#onSubscribe(Subscription)} * @param onRequest - * a LongConsumer called with the request amount sent via Subscription.request() + * a {@link LongConsumer} called with the request amount sent via {@link Subscription#request(long)} * @param onCancel - * called when the downstream cancels the Subscription via cancel() - * @return the source Publisher with the side-effecting behavior applied + * called when the downstream cancels the {@code Subscription} via {@link Subscription#cancel()} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onSubscribe}, {@code onRequest} or {@code onCancel} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnLifecycle(final Consumer onSubscribe, - final LongConsumer onRequest, final Action onCancel) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - ObjectHelper.requireNonNull(onRequest, "onRequest is null"); - ObjectHelper.requireNonNull(onCancel, "onCancel is null"); - return RxJavaPlugins.onAssembly(new FlowableDoOnLifecycle(this, onSubscribe, onRequest, onCancel)); + public final Flowable doOnLifecycle(@NonNull Consumer onSubscribe, + @NonNull LongConsumer onRequest, @NonNull Action onCancel) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + Objects.requireNonNull(onRequest, "onRequest is null"); + Objects.requireNonNull(onCancel, "onCancel is null"); + return RxJavaPlugins.onAssembly(new FlowableDoOnLifecycle<>(this, onSubscribe, onRequest, onCancel)); } /** - * Modifies the source Publisher so that it invokes an action when it calls {@code onNext}. + * Calls the given {@link Consumer} with the value emitted by the current {@code Flowable} before forwarding it to the downstream. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnNext} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the action to invoke when the source Publisher calls {@code onNext} - * @return the source Publisher with the side-effecting behavior applied + * the action to invoke when the current {@code Flowable} calls {@code onNext} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Do + * @see #doAfterNext(Consumer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnNext(Consumer onNext) { + @NonNull + public final Flowable doOnNext(@NonNull Consumer onNext) { return doOnEach(onNext, Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Modifies the source {@code Publisher} so that it invokes the given action when it receives a - * request for more items. + * Calls the given {@link LongConsumer} with the request amount from the downstream before forwarding it + * to the current {@code Flowable}. *

* Note: This operator is for tracing the internal behavior of back-pressure request * patterns and generally intended for debugging use. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnRequest} does not operate by default on a particular {@link Scheduler}.
*
* * @param onRequest - * the action that gets called when a Subscriber requests items from this - * {@code Publisher} - * @return the source {@code Publisher} modified so as to call this Action when appropriate + * the action that gets called when a {@link Subscriber} requests items from the current + * {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onRequest} is {@code null} * @see ReactiveX operators * documentation: Do * @since 2.0 @@ -9231,103 +9988,108 @@ public final Flowable doOnNext(Consumer onNext) { @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnRequest(LongConsumer onRequest) { + @NonNull + public final Flowable doOnRequest(@NonNull LongConsumer onRequest) { return doOnLifecycle(Functions.emptyConsumer(), onRequest, Functions.EMPTY_ACTION); } /** - * Modifies the source {@code Publisher} so that it invokes the given action when it is subscribed from - * its subscribers. Each subscription will result in an invocation of the given action except when the - * source {@code Publisher} is reference counted, in which case the source {@code Publisher} will invoke - * the given action for the first subscription. + * Calls the given {@link Consumer} with the {@link Subscription} provided by the current {@code Flowable} upon + * subscription from the downstream before forwarding it to the subscriber's + * {@link Subscriber#onSubscribe(Subscription) onSubscribe} method. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnSubscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onSubscribe - * the Consumer that gets called when a Subscriber subscribes to the current {@code Flowable} - * @return the source {@code Publisher} modified so as to call this Consumer when appropriate + * the {@code Consumer} that gets called when a {@link Subscriber} subscribes to the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnSubscribe(Consumer onSubscribe) { + @NonNull + public final Flowable doOnSubscribe(@NonNull Consumer onSubscribe) { return doOnLifecycle(onSubscribe, Functions.EMPTY_LONG_CONSUMER, Functions.EMPTY_ACTION); } /** - * Modifies the source Publisher so that it invokes an action when it calls {@code onComplete} or - * {@code onError}. + * Calls the given {@link Action} when the current {@code Flowable} completes normally or with an error before those signals + * are forwarded to the downstream. *

- * + * *

* This differs from {@code doAfterTerminate} in that this happens before the {@code onComplete} or * {@code onError} notification. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s * backpressure behavior.
*
Scheduler:
*
{@code doOnTerminate} does not operate by default on a particular {@link Scheduler}.
*
* * @param onTerminate - * the action to invoke when the source Publisher calls {@code onComplete} or {@code onError} - * @return the source Publisher with the side-effecting behavior applied + * the action to invoke when the current {@code Flowable} calls {@code onComplete} or {@code onError} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @see #doAfterTerminate(Action) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable doOnTerminate(final Action onTerminate) { + @NonNull + public final Flowable doOnTerminate(@NonNull Action onTerminate) { return doOnEach(Functions.emptyConsumer(), Functions.actionConsumer(onTerminate), onTerminate, Functions.EMPTY_ACTION); } /** - * Returns a Maybe that emits the single item at a specified index in a sequence of emissions from - * this Flowable or completes if this Flowable sequence has fewer elements than index. + * Returns a {@link Maybe} that emits the single item at a specified index in a sequence of emissions from + * this {@code Flowable} or completes if this {@code Flowable} sequence has fewer elements than index. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in a bounded manner.
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
*
Scheduler:
*
{@code elementAt} does not operate by default on a particular {@link Scheduler}.
*
* * @param index * the zero-based index of the item to retrieve - * @return a Maybe that emits a single item: the item at the specified position in the sequence of - * those emitted by the source Publisher + * @return the new {@code Maybe} instance + * @throws IndexOutOfBoundsException if {@code index} is negative * @see ReactiveX operators documentation: ElementAt */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe elementAt(long index) { if (index < 0) { throw new IndexOutOfBoundsException("index >= 0 required but it was " + index); } - return RxJavaPlugins.onAssembly(new FlowableElementAtMaybe(this, index)); + return RxJavaPlugins.onAssembly(new FlowableElementAtMaybe<>(this, index)); } /** - * Returns a Single that emits the item found at a specified index in a sequence of emissions from - * this Flowable, or a default item if that index is out of range. + * Returns a {@link Single} that emits the item found at a specified index in a sequence of emissions from + * this {@code Flowable}, or a default item if that index is out of range. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in a bounded manner.
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
*
Scheduler:
*
{@code elementAt} does not operate by default on a particular {@link Scheduler}.
*
@@ -9336,40 +10098,39 @@ public final Maybe elementAt(long index) { * the zero-based index of the item to retrieve * @param defaultItem * the default item - * @return a Single that emits the item at the specified position in the sequence emitted by the source - * Publisher, or the default item if that index is outside the bounds of the source sequence + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @throws IndexOutOfBoundsException - * if {@code index} is less than 0 + * if {@code index} is negative * @see ReactiveX operators documentation: ElementAt */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Single elementAt(long index, T defaultItem) { + public final Single elementAt(long index, @NonNull T defaultItem) { if (index < 0) { throw new IndexOutOfBoundsException("index >= 0 required but it was " + index); } - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); - return RxJavaPlugins.onAssembly(new FlowableElementAtSingle(this, index, defaultItem)); + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new FlowableElementAtSingle<>(this, index, defaultItem)); } /** - * Returns a Single that emits the item found at a specified index in a sequence of emissions from - * this Flowable or signals a {@link NoSuchElementException} if this Flowable has fewer elements than index. + * Returns a {@link Single} that emits the item found at a specified index in a sequence of emissions from + * this {@code Flowable} or signals a {@link NoSuchElementException} if this {@code Flowable} has fewer elements than index. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in a bounded manner.
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
*
Scheduler:
*
{@code elementAtOrError} does not operate by default on a particular {@link Scheduler}.
*
* * @param index * the zero-based index of the item to retrieve - * @return a Single that emits the item at the specified position in the sequence emitted by the source - * Publisher, or the default item if that index is outside the bounds of the source sequence + * @return the new {@code Single} instance * @throws IndexOutOfBoundsException * if {@code index} is less than 0 * @see ReactiveX operators documentation: ElementAt @@ -9377,283 +10138,289 @@ public final Single elementAt(long index, T defaultItem) { @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single elementAtOrError(long index) { if (index < 0) { throw new IndexOutOfBoundsException("index >= 0 required but it was " + index); } - return RxJavaPlugins.onAssembly(new FlowableElementAtSingle(this, index, null)); + return RxJavaPlugins.onAssembly(new FlowableElementAtSingle<>(this, index, null)); } /** - * Filters items emitted by a Publisher by only emitting those that satisfy a specified predicate. + * Filters items emitted by the current {@code Flowable} by only emitting those that satisfy a specified predicate. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code filter} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates each item emitted by the source Publisher, returning {@code true} + * a function that evaluates each item emitted by the current {@code Flowable}, returning {@code true} * if it passes the filter - * @return a Flowable that emits only those items emitted by the source Publisher that the filter - * evaluates as {@code true} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable filter(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableFilter(this, predicate)); + public final Flowable filter(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new FlowableFilter<>(this, predicate)); } /** - * Returns a Maybe that emits only the very first item emitted by this Flowable or - * completes if this Flowable is empty. + * Returns a {@link Maybe} that emits only the very first item emitted by this {@code Flowable} or + * completes if this {@code Flowable} is empty. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in a bounded manner.
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
*
Scheduler:
*
{@code firstElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Maybe instance + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe firstElement() { return elementAt(0); } /** - * Returns a Single that emits only the very first item emitted by this Flowable, or a default - * item if this Flowable completes without emitting anything. + * Returns a {@link Single} that emits only the very first item emitted by this {@code Flowable}, or a default + * item if this {@code Flowable} completes without emitting anything. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in a bounded manner.
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
*
Scheduler:
*
{@code first} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the default item to emit if the source Publisher doesn't emit anything - * @return a Single that emits only the very first item from the source, or a default item if the - * source Publisher completes without emitting any items + * the default item to emit if the current {@code Flowable} doesn't emit anything + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Single first(T defaultItem) { + @NonNull + public final Single first(@NonNull T defaultItem) { return elementAt(0, defaultItem); } /** - * Returns a Single that emits only the very first item emitted by this Flowable or - * signals a {@link NoSuchElementException} if this Flowable is empty. + * Returns a {@link Single} that emits only the very first item emitted by this {@code Flowable} or + * signals a {@link NoSuchElementException} if this {@code Flowable} is empty. *

* *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in a bounded manner.
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in a bounded manner.
*
Scheduler:
*
{@code firstOrError} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Single instance + * @return the new {@code Single} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) // take may trigger UNBOUNDED_IN @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single firstOrError() { return elementAtOrError(0); } /** - * Returns a Flowable that emits items based on applying a function that you supply to each item emitted - * by the source Publisher, where that function returns a Publisher, and then merging those resulting - * Publishers and emitting the results of this merger. + * Returns a {@code Flowable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Flowable}, where that function returns a {@link Publisher}, and then merging those resulting + * {@code Publisher}s and emitting the results of this merger. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@link #bufferSize()} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner Publishers and the output type + * @param the value type of the inner {@code Publisher}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and merging the results of the Publishers obtained from this - * transformation + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable flatMap(@NonNull Function> mapper) { return flatMap(mapper, false, bufferSize(), bufferSize()); } /** - * Returns a Flowable that emits items based on applying a function that you supply to each item emitted - * by the source Publisher, where that function returns a Publisher, and then merging those resulting - * Publishers and emitting the results of this merger. + * Returns a {@code Flowable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Flowable}, where that function returns a {@link Publisher}, and then merging those resulting + * {@code Publisher}s and emitting the results of this merger. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@link #bufferSize()} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner Publishers and the output type + * @param the value type of the inner {@code Publisher}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param delayErrors - * if true, exceptions from the current Flowable and all inner Publishers are delayed until all of them terminate - * if false, the first one signaling an exception will terminate the whole sequence immediately - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and merging the results of the Publishers obtained from this - * transformation + * if {@code true}, exceptions from the current {@code Flowable} and all inner {@code Publisher}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, boolean delayErrors) { + @NonNull + public final <@NonNull R> Flowable flatMap(@NonNull Function> mapper, boolean delayErrors) { return flatMap(mapper, delayErrors, bufferSize(), bufferSize()); } /** - * Returns a Flowable that emits items based on applying a function that you supply to each item emitted - * by the source Publisher, where that function returns a Publisher, and then merging those resulting - * Publishers and emitting the results of this merger, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Returns a {@code Flowable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Flowable}, where that function returns a {@link Publisher}, and then merging those resulting + * {@code Publisher}s and emitting the results of this merger, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner Publishers and the output type + * @param the value type of the inner {@code Publisher}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and merging the results of the Publishers obtained from this - * transformation + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, int maxConcurrency) { + @NonNull + public final <@NonNull R> Flowable flatMap(@NonNull Function> mapper, int maxConcurrency) { return flatMap(mapper, false, maxConcurrency, bufferSize()); } /** - * Returns a Flowable that emits items based on applying a function that you supply to each item emitted - * by the source Publisher, where that function returns a Publisher, and then merging those resulting - * Publishers and emitting the results of this merger, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Returns a {@code Flowable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Flowable}, where that function returns a {@link Publisher}, and then merging those resulting + * {@code Publisher}s and emitting the results of this merger, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner Publishers and the output type + * @param the value type of the inner {@code Publisher}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Flowable and all inner Publishers are delayed until all of them terminate - * if false, the first one signaling an exception will terminate the whole sequence immediately - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and merging the results of the Publishers obtained from this - * transformation + * if {@code true}, exceptions from the current {@code Flowable} and all inner {@code Publisher}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, boolean delayErrors, int maxConcurrency) { + @NonNull + public final <@NonNull R> Flowable flatMap(@NonNull Function> mapper, boolean delayErrors, int maxConcurrency) { return flatMap(mapper, delayErrors, maxConcurrency, bufferSize()); } /** - * Returns a Flowable that emits items based on applying a function that you supply to each item emitted - * by the source Publisher, where that function returns a Publisher, and then merging those resulting - * Publishers and emitting the results of this merger, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Returns a {@code Flowable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Flowable}, where that function returns a {@link Publisher}, and then merging those resulting + * {@code Publisher}s and emitting the results of this merger, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner Publishers and the output type + * @param the value type of the inner {@code Publisher}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Flowable and all inner Publishers are delayed until all of them terminate - * if false, the first one signaling an exception will terminate the whole sequence immediately + * if {@code true}, exceptions from the current {@code Flowable} and all inner {@code Publisher}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately * @param bufferSize - * the number of elements to prefetch from each inner Publisher - * @return a Flowable that emits the result of applying the transformation function to each item emitted - * by the source Publisher and merging the results of the Publishers obtained from this - * transformation + * the number of elements to prefetch from each inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @@ -9661,9 +10428,9 @@ public final Flowable flatMap(Function Flowable flatMap(Function> mapper, + public final <@NonNull R> Flowable flatMap(@NonNull Function> mapper, boolean delayErrors, int maxConcurrency, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @@ -9674,20 +10441,20 @@ public final Flowable flatMap(Function(this, mapper, delayErrors, maxConcurrency, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableFlatMap<>(this, mapper, delayErrors, maxConcurrency, bufferSize)); } /** - * Returns a Flowable that applies a function to each item emitted or notification raised by the source - * Publisher and then flattens the Publishers returned from these functions and emits the resulting items. + * Returns a {@code Flowable} that applies a function to each item emitted or notification raised by the current + * {@code Flowable} and then flattens the {@link Publisher}s returned from these functions and emits the resulting items. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@link #bufferSize()} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
@@ -9695,43 +10462,43 @@ public final Flowable flatMap(Function * the result type * @param onNextMapper - * a function that returns a Publisher to merge for each item emitted by the source Publisher + * a function that returns a {@code Publisher} to merge for each item emitted by the current {@code Flowable} * @param onErrorMapper - * a function that returns a Publisher to merge for an onError notification from the source - * Publisher + * a function that returns a {@code Publisher} to merge for an {@code onError} notification from the current + * {@code Flowable} * @param onCompleteSupplier - * a function that returns a Publisher to merge for an onComplete notification from the source - * Publisher - * @return a Flowable that emits the results of merging the Publishers returned from applying the - * specified functions to the emissions and notifications of the source Publisher + * a function that returns a {@code Publisher} to merge for an {@code onComplete} notification from the current + * {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onNextMapper}, {@code onErrorMapper} or {@code onCompleteSupplier} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap( - Function> onNextMapper, - Function> onErrorMapper, - Supplier> onCompleteSupplier) { - ObjectHelper.requireNonNull(onNextMapper, "onNextMapper is null"); - ObjectHelper.requireNonNull(onErrorMapper, "onErrorMapper is null"); - ObjectHelper.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); - return merge(new FlowableMapNotification>(this, onNextMapper, onErrorMapper, onCompleteSupplier)); + public final <@NonNull R> Flowable flatMap( + @NonNull Function> onNextMapper, + @NonNull Function> onErrorMapper, + @NonNull Supplier> onCompleteSupplier) { + Objects.requireNonNull(onNextMapper, "onNextMapper is null"); + Objects.requireNonNull(onErrorMapper, "onErrorMapper is null"); + Objects.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); + return merge(new FlowableMapNotification<>(this, onNextMapper, onErrorMapper, onCompleteSupplier)); } /** - * Returns a Flowable that applies a function to each item emitted or notification raised by the source - * Publisher and then flattens the Publishers returned from these functions and emits the resulting items, - * while limiting the maximum number of concurrent subscriptions to these Publishers. + * Returns a {@code Flowable} that applies a function to each item emitted or notification raised by the current + * {@code Flowable} and then flattens the {@link Publisher}s returned from these functions and emits the resulting items, + * while limiting the maximum number of concurrent subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
@@ -9739,17 +10506,18 @@ public final Flowable flatMap( * @param * the result type * @param onNextMapper - * a function that returns a Publisher to merge for each item emitted by the source Publisher + * a function that returns a {@code Publisher} to merge for each item emitted by the current {@code Flowable} * @param onErrorMapper - * a function that returns a Publisher to merge for an onError notification from the source - * Publisher + * a function that returns a {@code Publisher} to merge for an {@code onError} notification from the current + * {@code Flowable} * @param onCompleteSupplier - * a function that returns a Publisher to merge for an onComplete notification from the source - * Publisher + * a function that returns a {@code Publisher} to merge for an {@code onComplete} notification from the current + * {@code Flowable} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits the results of merging the Publishers returned from applying the - * specified functions to the emissions and notifications of the source Publisher + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onNextMapper}, {@code onErrorMapper} or {@code onCompleteSupplier} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @@ -9757,170 +10525,175 @@ public final Flowable flatMap( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap( - Function> onNextMapper, - Function> onErrorMapper, - Supplier> onCompleteSupplier, + public final <@NonNull R> Flowable flatMap( + @NonNull Function> onNextMapper, + @NonNull Function> onErrorMapper, + @NonNull Supplier> onCompleteSupplier, int maxConcurrency) { - ObjectHelper.requireNonNull(onNextMapper, "onNextMapper is null"); - ObjectHelper.requireNonNull(onErrorMapper, "onErrorMapper is null"); - ObjectHelper.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); - return merge(new FlowableMapNotification>( + Objects.requireNonNull(onNextMapper, "onNextMapper is null"); + Objects.requireNonNull(onErrorMapper, "onErrorMapper is null"); + Objects.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); + return merge(new FlowableMapNotification<>( this, onNextMapper, onErrorMapper, onCompleteSupplier), maxConcurrency); } /** - * Returns a Flowable that emits the results of a specified function to the pair of values emitted by the - * source Publisher and a specified collection Publisher. + * Returns a {@code Flowable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Flowable} and a specified collection {@link Publisher}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the inner Publishers + * the type of items emitted by the inner {@code Publisher}s * @param * the type of items emitted by the combiner function * @param mapper - * a function that returns a Publisher for each item emitted by the source Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} * @param combiner - * a function that combines one item emitted by each of the source and collection Publishers and - * returns an item to be emitted by the resulting Publisher - * @return a Flowable that emits the results of applying a function to a pair of values emitted by the - * source Publisher and the collection Publisher + * a function that combines one item emitted by each of the source and collection {@code Publisher}s and + * returns an item to be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, - BiFunction combiner) { + @NonNull + public final <@NonNull U, @NonNull R> Flowable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner) { return flatMap(mapper, combiner, false, bufferSize(), bufferSize()); } /** - * Returns a Flowable that emits the results of a specified function to the pair of values emitted by the - * source Publisher and a specified collection Publisher. + * Returns a {@code Flowable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Flowable} and a specified inner {@link Publisher}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@link #bufferSize()} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the inner Publishers + * the type of items emitted by the inner {@code Publisher}s * @param * the type of items emitted by the combiner functions * @param mapper - * a function that returns a Publisher for each item emitted by the source Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} * @param combiner - * a function that combines one item emitted by each of the source and collection Publishers and - * returns an item to be emitted by the resulting Publisher + * a function that combines one item emitted by each of the source and collection {@code Publisher}s and + * returns an item to be emitted by the resulting {@code Flowable} * @param delayErrors - * if true, exceptions from the current Flowable and all inner Publishers are delayed until all of them terminate - * if false, the first one signaling an exception will terminate the whole sequence immediately - * @return a Flowable that emits the results of applying a function to a pair of values emitted by the - * source Publisher and the collection Publisher + * if {@code true}, exceptions from the current {@code Flowable} and all inner {@code Publisher}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, - BiFunction combiner, boolean delayErrors) { + @NonNull + public final <@NonNull U, @NonNull R> Flowable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, boolean delayErrors) { return flatMap(mapper, combiner, delayErrors, bufferSize(), bufferSize()); } /** - * Returns a Flowable that emits the results of a specified function to the pair of values emitted by the - * source Publisher and a specified collection Publisher, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Returns a {@code Flowable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Flowable} and a specified collection {@link Publisher}, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the inner Publishers + * the type of items emitted by the inner {@code Publisher}s * @param * the type of items emitted by the combiner function * @param mapper - * a function that returns a Publisher for each item emitted by the source Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} * @param combiner - * a function that combines one item emitted by each of the source and collection Publishers and - * returns an item to be emitted by the resulting Publisher + * a function that combines one item emitted by each of the source and collection {@code Publisher}s and + * returns an item to be emitted by the resulting {@code Flowable} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Flowable and all inner Publishers are delayed until all of them terminate - * if false, the first one signaling an exception will terminate the whole sequence immediately - * @return a Flowable that emits the results of applying a function to a pair of values emitted by the - * source Publisher and the collection Publisher + * if {@code true}, exceptions from the current {@code Flowable} and all inner {@code Publisher}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, - BiFunction combiner, boolean delayErrors, int maxConcurrency) { + @NonNull + public final <@NonNull U, @NonNull R> Flowable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, boolean delayErrors, int maxConcurrency) { return flatMap(mapper, combiner, delayErrors, maxConcurrency, bufferSize()); } /** - * Returns a Flowable that emits the results of a specified function to the pair of values emitted by the - * source Publisher and a specified collection Publisher, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Returns a {@code Flowable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Flowable} and a specified collection {@link Publisher}, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@code maxConcurrency} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the inner Publishers + * the type of items emitted by the inner {@code Publisher}s * @param * the type of items emitted by the combiner function * @param mapper - * a function that returns a Publisher for each item emitted by the source Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} * @param combiner - * a function that combines one item emitted by each of the source and collection Publishers and - * returns an item to be emitted by the resulting Publisher + * a function that combines one item emitted by each of the source and collection {@code Publisher}s and + * returns an item to be emitted by the resulting {@code Flowable} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently + * the maximum number of {@code Publisher}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Flowable and all inner Publishers are delayed until all of them terminate - * if false, the first one signaling an exception will terminate the whole sequence immediately + * if {@code true}, exceptions from the current {@code Flowable} and all inner {@code Publisher}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately * @param bufferSize - * the number of elements to prefetch from the inner Publishers. - * @return a Flowable that emits the results of applying a function to a pair of values emitted by the - * source Publisher and the collection Publisher + * the number of elements to prefetch from the inner {@code Publisher}s. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @@ -9928,233 +10701,245 @@ public final Flowable flatMap(Function Flowable flatMap(final Function> mapper, - final BiFunction combiner, boolean delayErrors, int maxConcurrency, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + public final <@NonNull U, @NonNull R> Flowable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, boolean delayErrors, int maxConcurrency, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return flatMap(FlowableInternalHelper.flatMapWithCombiner(mapper, combiner), delayErrors, maxConcurrency, bufferSize); } /** - * Returns a Flowable that emits the results of a specified function to the pair of values emitted by the - * source Publisher and a specified collection Publisher, while limiting the maximum number of concurrent - * subscriptions to these Publishers. + * Returns a {@code Flowable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Flowable} and a specified collection {@link Publisher}, while limiting the maximum number of concurrent + * subscriptions to these {@code Publisher}s. * - * + * *
*
Backpressure:
- *
The operator honors backpressure from downstream. The upstream Flowable is consumed + *
The operator honors backpressure from downstream. The upstream {@code Flowable} is consumed * in a bounded manner (up to {@link #bufferSize()} outstanding request amount for items). * The inner {@code Publisher}s are expected to honor backpressure; if violated, - * the operator may signal {@code MissingBackpressureException}.
+ * the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the inner Publishers + * the type of items emitted by the inner {@code Publisher}s * @param * the type of items emitted by the combiner function * @param mapper - * a function that returns a Publisher for each item emitted by the source Publisher + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} * @param combiner - * a function that combines one item emitted by each of the source and collection Publishers and - * returns an item to be emitted by the resulting Publisher + * a function that combines one item emitted by each of the source and collection {@code Publisher}s and + * returns an item to be emitted by the resulting {@code Flowable} * @param maxConcurrency - * the maximum number of Publishers that may be subscribed to concurrently - * @return a Flowable that emits the results of applying a function to a pair of values emitted by the - * source Publisher and the collection Publisher + * the maximum number of {@code Publisher}s that may be subscribed to concurrently + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMap(Function> mapper, - BiFunction combiner, int maxConcurrency) { + @NonNull + public final <@NonNull U, @NonNull R> Flowable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, int maxConcurrency) { return flatMap(mapper, combiner, false, maxConcurrency, bufferSize()); } /** - * Maps each element of the upstream Flowable into CompletableSources, subscribes to them and - * waits until the upstream and all CompletableSources complete. + * Maps each element of the upstream {@code Flowable} into {@link CompletableSource}s, subscribes to them and + * waits until the upstream and all {@code CompletableSource}s complete. *
*
Backpressure:
*
The operator consumes the upstream in an unbounded manner.
*
Scheduler:
*
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
- * @param mapper the function that received each source value and transforms them into CompletableSources. - * @return the new Completable instance + * @param mapper the function that received each source value and transforms them into {@code CompletableSource}s. + * @return the new {@link Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Completable flatMapCompletable(Function mapper) { + @NonNull + public final Completable flatMapCompletable(@NonNull Function mapper) { return flatMapCompletable(mapper, false, Integer.MAX_VALUE); } /** - * Maps each element of the upstream Flowable into CompletableSources, subscribes to them and - * waits until the upstream and all CompletableSources complete, optionally delaying all errors. + * Maps each element of the upstream {@code Flowable} into {@link CompletableSource}s, subscribes to them and + * waits until the upstream and all {@code CompletableSource}s complete, optionally delaying all errors. *
*
Backpressure:
- *
If {@code maxConcurrency == Integer.MAX_VALUE} the operator consumes the upstream in an unbounded manner. + *
If {@code maxConcurrency == }{@link Integer#MAX_VALUE} the operator consumes the upstream in an unbounded manner. * Otherwise, the operator expects the upstream to honor backpressure. If the upstream doesn't support backpressure - * the operator behaves as if {@code maxConcurrency == Integer.MAX_VALUE} was used.
+ * the operator behaves as if {@code maxConcurrency == }{@link Integer#MAX_VALUE} was used. *
Scheduler:
*
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
- * @param mapper the function that received each source value and transforms them into CompletableSources. - * @param delayErrors if true errors from the upstream and inner CompletableSources are delayed until each of them + * @param mapper the function that received each source value and transforms them into {@code CompletableSource}s. + * @param delayErrors if {@code true}, errors from the upstream and inner {@code CompletableSource}s are delayed until each of them * terminates. - * @param maxConcurrency the maximum number of active subscriptions to the CompletableSources. - * @return the new Completable instance + * @param maxConcurrency the maximum number of active subscriptions to the {@code CompletableSource}s. + * @return the new {@link Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Completable flatMapCompletable(Function mapper, boolean delayErrors, int maxConcurrency) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final Completable flatMapCompletable(@NonNull Function mapper, boolean delayErrors, int maxConcurrency) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapCompletableCompletable(this, mapper, delayErrors, maxConcurrency)); + return RxJavaPlugins.onAssembly(new FlowableFlatMapCompletableCompletable<>(this, mapper, delayErrors, maxConcurrency)); } /** - * Returns a Flowable that merges each item emitted by the source Publisher with the values in an - * Iterable corresponding to that item that is generated by a selector. + * Merges {@link Iterable}s generated by a mapper {@link Function} for each individual item emitted by + * the current {@code Flowable} into a single {@code Flowable} sequence. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ *
The operator honors backpressure from downstream. The current {@code Flowable}s is + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
*
Scheduler:
*
{@code flatMapIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of item emitted by the resulting Iterable + * the output type and the element type of the {@code Iterable}s * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Publisher - * @return a Flowable that emits the results of merging the items emitted by the source Publisher with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapIterable(final Function> mapper) { + @NonNull + public final <@NonNull U> Flowable flatMapIterable(@NonNull Function> mapper) { return flatMapIterable(mapper, bufferSize()); } /** - * Returns a Flowable that merges each item emitted by the source Publisher with the values in an - * Iterable corresponding to that item that is generated by a selector. + * Merges {@link Iterable}s generated by a mapper {@link Function} for each individual item emitted by + * the current {@code Flowable} into a single {@code Flowable} sequence. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ *
The operator honors backpressure from downstream. The current {@code Flowable}s is + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
*
Scheduler:
*
{@code flatMapIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of item emitted by the resulting Iterable + * the type of item emitted by the resulting {@code Iterable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Publisher + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Flowable} * @param bufferSize - * the number of elements to prefetch from the current Flowable - * @return a Flowable that emits the results of merging the items emitted by the source Publisher with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapIterable(final Function> mapper, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull U> Flowable flatMapIterable(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableFlattenIterable(this, mapper, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableFlattenIterable<>(this, mapper, bufferSize)); } /** - * Returns a Flowable that emits the results of applying a function to the pair of values from the source - * Publisher and an Iterable corresponding to that item that is generated by a selector. + * Merges {@link Iterable}s generated by a mapper {@link Function} for each individual item emitted by + * the current {@code Flowable} into a single {@code Flowable} sequence where the resulting items will + * be the combination of the original item and each inner item of the respective {@code Iterable} as returned + * by the {@code resultSelector} {@link BiFunction}. *

* *

*
Backpressure:
- *
The operator honors backpressure from downstream and the source {@code Publisher}s is - * consumed in an unbounded manner (i.e., no backpressure is applied to it).
+ *
The operator honors backpressure from downstream and the current {@code Flowable}s is + * consumed in a bounded manner (requesting {@link #bufferSize()} items upfront, then 75% of it after 75% received).
*
Scheduler:
*
{@code flatMapIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the collection element type + * the element type of the {@code Iterable}s * @param - * the type of item emitted by the resulting Iterable + * the output type as determined by the {@code resultSelector} function * @param mapper - * a function that returns an Iterable sequence of values for each item emitted by the source - * Publisher - * @param resultSelector - * a function that returns an item based on the item emitted by the source Publisher and the - * Iterable returned for that item by the {@code collectionSelector} - * @return a Flowable that emits the items returned by {@code resultSelector} for each item in the source - * Publisher + * a function that returns an {@code Iterable} sequence of values for each item emitted by the current + * {@code Flowable} + * @param combiner + * a function that returns an item based on the item emitted by the current {@code Flowable} and the + * {@code Iterable} returned for that item by the {@code collectionSelector} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapIterable(final Function> mapper, - final BiFunction resultSelector) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); - return flatMap(FlowableInternalHelper.flatMapIntoIterable(mapper), resultSelector, false, bufferSize(), bufferSize()); + public final <@NonNull U, @NonNull V> Flowable flatMapIterable(@NonNull Function> mapper, + @NonNull BiFunction combiner) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return flatMap(FlowableInternalHelper.flatMapIntoIterable(mapper), combiner, false, bufferSize(), bufferSize()); } /** - * Returns a Flowable that merges each item emitted by the source Publisher with the values in an - * Iterable corresponding to that item that is generated by a selector, while limiting the number of concurrent - * subscriptions to these Publishers. + * Merges {@link Iterable}s generated by a mapper {@link Function} for each individual item emitted by + * the current {@code Flowable} into a single {@code Flowable} sequence where the resulting items will + * be the combination of the original item and each inner item of the respective {@code Iterable} as returned + * by the {@code resultSelector} {@link BiFunction}. *

* *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is - * expected to honor backpressure as well. If the source {@code Publisher} violates the rule, the operator will - * signal a {@code MissingBackpressureException}.
+ *
The operator honors backpressure from downstream. The current {@code Flowable}s is + * expected to honor backpressure as well. If the current {@code Flowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
*
Scheduler:
*
{@code flatMapIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the element type of the inner Iterable sequences + * the element type of the inner {@code Iterable} sequences * @param - * the type of item emitted by the resulting Publisher + * the type of item emitted by the resulting {@code Flowable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Publisher - * @param resultSelector - * a function that returns an item based on the item emitted by the source Publisher and the - * Iterable returned for that item by the {@code collectionSelector} + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Flowable} + * @param combiner + * a function that returns an item based on the item emitted by the current {@code Flowable} and the + * {@code Iterable} returned for that item by the {@code collectionSelector} * @param prefetch - * the number of elements to prefetch from the current Flowable - * @return a Flowable that emits the results of merging the items emitted by the source Publisher with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @@ -10162,16 +10947,18 @@ public final Flowable flatMapIterable(final Function Flowable flatMapIterable(final Function> mapper, - final BiFunction resultSelector, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); - return flatMap(FlowableInternalHelper.flatMapIntoIterable(mapper), resultSelector, false, bufferSize(), prefetch); + public final <@NonNull U, @NonNull V> Flowable flatMapIterable(@NonNull Function> mapper, + @NonNull BiFunction combiner, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return flatMap(FlowableInternalHelper.flatMapIntoIterable(mapper), combiner, false, bufferSize(), prefetch); } /** - * Maps each element of the upstream Flowable into MaybeSources, subscribes to all of them - * and merges their onSuccess values, in no particular order, into a single Flowable sequence. + * Maps each element of the upstream {@code Flowable} into {@link MaybeSource}s, subscribes to all of them + * and merges their {@code onSuccess} values, in no particular order, into a single {@code Flowable} sequence. + *

+ * *

*
Backpressure:
*
The operator consumes the upstream in an unbounded manner.
@@ -10179,48 +10966,54 @@ public final Flowable flatMapIterable(final Function{@code flatMapMaybe} does not operate by default on a particular {@link Scheduler}. *
* @param the result value type - * @param mapper the function that received each source value and transforms them into MaybeSources. - * @return the new Flowable instance + * @param mapper the function that received each source value and transforms them into {@code MaybeSource}s. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapMaybe(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable flatMapMaybe(@NonNull Function> mapper) { return flatMapMaybe(mapper, false, Integer.MAX_VALUE); } /** - * Maps each element of the upstream Flowable into MaybeSources, subscribes to at most - * {@code maxConcurrency} MaybeSources at a time and merges their onSuccess values, - * in no particular order, into a single Flowable sequence, optionally delaying all errors. + * Maps each element of the upstream {@code Flowable} into {@link MaybeSource}s, subscribes to at most + * {@code maxConcurrency} {@code MaybeSource}s at a time and merges their {@code onSuccess} values, + * in no particular order, into a single {@code Flowable} sequence, optionally delaying all errors. + *

+ * *

*
Backpressure:
- *
If {@code maxConcurrency == Integer.MAX_VALUE} the operator consumes the upstream in an unbounded manner. + *
If {@code maxConcurrency == }{@link Integer#MAX_VALUE} the operator consumes the upstream in an unbounded manner. * Otherwise, the operator expects the upstream to honor backpressure. If the upstream doesn't support backpressure - * the operator behaves as if {@code maxConcurrency == Integer.MAX_VALUE} was used.
+ * the operator behaves as if {@code maxConcurrency == }{@link Integer#MAX_VALUE} was used. *
Scheduler:
*
{@code flatMapMaybe} does not operate by default on a particular {@link Scheduler}.
*
* @param the result value type - * @param mapper the function that received each source value and transforms them into MaybeSources. - * @param delayErrors if true errors from the upstream and inner MaybeSources are delayed until each of them + * @param mapper the function that received each source value and transforms them into {@code MaybeSource}s. + * @param delayErrors if {@code true}, errors from the upstream and inner {@code MaybeSource}s are delayed until each of them * terminates. - * @param maxConcurrency the maximum number of active subscriptions to the MaybeSources. - * @return the new Flowable instance + * @param maxConcurrency the maximum number of active subscriptions to the {@code MaybeSource}s. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapMaybe(Function> mapper, boolean delayErrors, int maxConcurrency) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable flatMapMaybe(@NonNull Function> mapper, boolean delayErrors, int maxConcurrency) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapMaybe(this, mapper, delayErrors, maxConcurrency)); + return RxJavaPlugins.onAssembly(new FlowableFlatMapMaybe<>(this, mapper, delayErrors, maxConcurrency)); } /** - * Maps each element of the upstream Flowable into SingleSources, subscribes to all of them - * and merges their onSuccess values, in no particular order, into a single Flowable sequence. + * Maps each element of the upstream {@code Flowable} into {@link SingleSource}s, subscribes to all of them + * and merges their {@code onSuccess} values, in no particular order, into a single {@code Flowable} sequence. *
*
Backpressure:
*
The operator consumes the upstream in an unbounded manner.
@@ -10228,52 +11021,56 @@ public final Flowable flatMapMaybe(Function{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}. *
* @param the result value type - * @param mapper the function that received each source value and transforms them into SingleSources. - * @return the new Flowable instance + * @param mapper the function that received each source value and transforms them into {@code SingleSource}s. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapSingle(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable flatMapSingle(@NonNull Function> mapper) { return flatMapSingle(mapper, false, Integer.MAX_VALUE); } /** - * Maps each element of the upstream Flowable into SingleSources, subscribes to at most - * {@code maxConcurrency} SingleSources at a time and merges their onSuccess values, - * in no particular order, into a single Flowable sequence, optionally delaying all errors. + * Maps each element of the upstream {@code Flowable} into {@link SingleSource}s, subscribes to at most + * {@code maxConcurrency} {@code SingleSource}s at a time and merges their {@code onSuccess} values, + * in no particular order, into a single {@code Flowable} sequence, optionally delaying all errors. *
*
Backpressure:
- *
If {@code maxConcurrency == Integer.MAX_VALUE} the operator consumes the upstream in an unbounded manner. + *
If {@code maxConcurrency == }{@link Integer#MAX_VALUE} the operator consumes the upstream in an unbounded manner. * Otherwise, the operator expects the upstream to honor backpressure. If the upstream doesn't support backpressure - * the operator behaves as if {@code maxConcurrency == Integer.MAX_VALUE} was used.
+ * the operator behaves as if {@code maxConcurrency == }{@link Integer#MAX_VALUE} was used. *
Scheduler:
*
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
*
* @param the result value type - * @param mapper the function that received each source value and transforms them into SingleSources. - * @param delayErrors if true errors from the upstream and inner SingleSources are delayed until each of them + * @param mapper the function that received each source value and transforms them into {@code SingleSource}s. + * @param delayErrors if {@code true}, errors from the upstream and inner {@code SingleSources} are delayed until each of them * terminates. - * @param maxConcurrency the maximum number of active subscriptions to the SingleSources. - * @return the new Flowable instance + * @param maxConcurrency the maximum number of active subscriptions to the {@code SingleSource}s. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapSingle(Function> mapper, boolean delayErrors, int maxConcurrency) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + public final <@NonNull R> Flowable flatMapSingle(@NonNull Function> mapper, boolean delayErrors, int maxConcurrency) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapSingle(this, mapper, delayErrors, maxConcurrency)); + return RxJavaPlugins.onAssembly(new FlowableFlatMapSingle<>(this, mapper, delayErrors, maxConcurrency)); } /** - * Subscribes to the {@link Publisher} and receives notifications for each element. + * Subscribes to the current {@code Flowable} and receives notifications for each element. *

* Alias to {@link #subscribe(Consumer)} *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code forEach} does not operate by default on a particular {@link Scheduler}.
@@ -10282,28 +11079,29 @@ public final Flowable flatMapSingle(FunctionReactiveX operators documentation: Subscribe */ @CheckReturnValue @BackpressureSupport(BackpressureKind.NONE) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEach(Consumer onNext) { + @NonNull + public final Disposable forEach(@NonNull Consumer onNext) { return subscribe(onNext); } /** - * Subscribes to the {@link Publisher} and receives notifications for each element until the - * onNext Predicate returns false. + * Subscribes to the current {@code Flowable} and receives notifications for each element until the + * {@code onNext} Predicate returns {@code false}. *

- * If the Flowable emits an error, it is wrapped into an + * If the {@code Flowable} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code forEachWhile} does not operate by default on a particular {@link Scheduler}.
@@ -10314,22 +11112,23 @@ public final Disposable forEach(Consumer onNext) { * @return * a {@link Disposable} that allows canceling an asynchronous sequence * @throws NullPointerException - * if {@code onNext} is null + * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @BackpressureSupport(BackpressureKind.NONE) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEachWhile(Predicate onNext) { + @NonNull + public final Disposable forEachWhile(@NonNull Predicate onNext) { return forEachWhile(onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to the {@link Publisher} and receives notifications for each element and error events until the - * onNext Predicate returns false. + * Subscribes to the current {@code Flowable} and receives notifications for each element and error events until the + * {@code onNext} Predicate returns {@code false}. *
*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code forEachWhile} does not operate by default on a particular {@link Scheduler}.
@@ -10342,23 +11141,23 @@ public final Disposable forEachWhile(Predicate onNext) { * @return * a {@link Disposable} that allows canceling an asynchronous sequence * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null + * if {@code onNext} or {@code onError} is {@code null} * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @BackpressureSupport(BackpressureKind.NONE) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEachWhile(Predicate onNext, Consumer onError) { + @NonNull + public final Disposable forEachWhile(@NonNull Predicate onNext, @NonNull Consumer onError) { return forEachWhile(onNext, onError, Functions.EMPTY_ACTION); } /** - * Subscribes to the {@link Publisher} and receives notifications for each element and the terminal events until the - * onNext Predicate returns false. + * Subscribes to the current {@code Flowable} and receives notifications for each element and the terminal events until the + * {@code onNext} Predicate returns {@code false}. *
*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code forEachWhile} does not operate by default on a particular {@link Scheduler}.
@@ -10373,51 +11172,49 @@ public final Disposable forEachWhile(Predicate onNext, ConsumerReactiveX operators documentation: Subscribe */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.NONE) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEachWhile(final Predicate onNext, final Consumer onError, - final Action onComplete) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + public final Disposable forEachWhile(@NonNull Predicate onNext, @NonNull Consumer onError, + @NonNull Action onComplete) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); - ForEachWhileSubscriber s = new ForEachWhileSubscriber(onNext, onError, onComplete); + ForEachWhileSubscriber s = new ForEachWhileSubscriber<>(onNext, onError, onComplete); subscribe(s); return s; } /** - * Groups the items emitted by a {@code Publisher} according to a specified criterion, and emits these - * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedPublisher} allows only a single + * Groups the items emitted by the current {@code Flowable} according to a specified criterion, and emits these + * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedFlowable} allows only a single * {@link Subscriber} during its lifetime and if this {@code Subscriber} cancels before the * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedPublisher} emission. + * {@code GroupedFlowable} emission. *

- * + * *

- * Note: A {@link GroupedFlowable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedFlowable} 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 - * {@code GroupedPublisher}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedFlowable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

- * Note that the {@link GroupedFlowable}s should be subscribed to as soon as possible, otherwise, + * Note that the {@code GroupedFlowable}s should be subscribed to as soon as possible, otherwise, * the unconsumed groups may starve other groups due to the internal backpressure * coordination of the {@code groupBy} operator. Such hangs can be usually avoided by using * {@link #flatMap(Function, int)} or {@link #concatMapEager(Function, int, int)} and overriding the default maximum concurrency * value to be greater or equal to the expected number of groups, possibly using - * {@code Integer.MAX_VALUE} if the number of expected groups is unknown. + * {@link Integer#MAX_VALUE} if the number of expected groups is unknown. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Backpressure:
@@ -10437,9 +11234,8 @@ public final Disposable forEachWhile(final Predicate onNext, final Co * a function that extracts the key for each item * @param * the key type - * @return a {@code Publisher} that emits {@link GroupedFlowable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source Publisher that share that - * key value + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: GroupBy * @see #groupBy(Function, boolean) * @see #groupBy(Function, Function) @@ -10447,35 +11243,36 @@ public final Disposable forEachWhile(final Predicate onNext, final Co @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> groupBy(Function keySelector) { - return groupBy(keySelector, Functions.identity(), false, bufferSize()); + @NonNull + public final <@NonNull K> Flowable> groupBy(@NonNull Function keySelector) { + return groupBy(keySelector, Functions.identity(), false, bufferSize()); } /** - * Groups the items emitted by a {@code Publisher} according to a specified criterion, and emits these - * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedPublisher} allows only a single + * Groups the items emitted by the current {@code Flowable} according to a specified criterion, and emits these + * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedFlowable} allows only a single * {@link Subscriber} during its lifetime and if this {@code Subscriber} cancels before the * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedPublisher} emission. + * {@code GroupedFlowable} emission. *

- * + * *

- * Note: A {@link GroupedFlowable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedFlowable} 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 - * {@code GroupedPublisher}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedFlowable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

- * Note that the {@link GroupedFlowable}s should be subscribed to as soon as possible, otherwise, + * Note that the {@code GroupedFlowable}s should be subscribed to as soon as possible, otherwise, * the unconsumed groups may starve other groups due to the internal backpressure * coordination of the {@code groupBy} operator. Such hangs can be usually avoided by using * {@link #flatMap(Function, int)} or {@link #concatMapEager(Function, int, int)} and overriding the default maximum concurrency * value to be greater or equal to the expected number of groups, possibly using - * {@code Integer.MAX_VALUE} if the number of expected groups is unknown. + * {@link Integer#MAX_VALUE} if the number of expected groups is unknown. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Backpressure:
@@ -10496,45 +11293,45 @@ public final Flowable> groupBy(Function * the key type * @param delayError - * if true, the exception from the current Flowable is delayed in each group until that specific group emitted - * the normal values; if false, the exception bypasses values in the groups and is reported immediately. - * @return a {@code Publisher} that emits {@link GroupedFlowable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source Publisher that share that - * key value + * if {@code true}, the exception from the current {@code Flowable} is delayed in each group until that specific group emitted + * the normal values; if {@code false}, the exception bypasses values in the groups and is reported immediately. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: GroupBy */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> groupBy(Function keySelector, boolean delayError) { - return groupBy(keySelector, Functions.identity(), delayError, bufferSize()); + @NonNull + public final <@NonNull K> Flowable> groupBy(@NonNull Function keySelector, boolean delayError) { + return groupBy(keySelector, Functions.identity(), delayError, bufferSize()); } /** - * Groups the items emitted by a {@code Publisher} according to a specified criterion, and emits these - * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedPublisher} allows only a single + * Groups the items emitted by the current {@code Flowable} according to a specified criterion, and emits these + * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedFlowable} allows only a single * {@link Subscriber} during its lifetime and if this {@code Subscriber} cancels before the * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedPublisher} emission. + * {@code GroupedFlowable} emission. *

- * + * *

- * Note: A {@link GroupedFlowable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedFlowable} 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 - * {@code GroupedPublisher}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedFlowable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

- * Note that the {@link GroupedFlowable}s should be subscribed to as soon as possible, otherwise, + * Note that the {@code GroupedFlowable}s should be subscribed to as soon as possible, otherwise, * the unconsumed groups may starve other groups due to the internal backpressure * coordination of the {@code groupBy} operator. Such hangs can be usually avoided by using * {@link #flatMap(Function, int)} or {@link #concatMapEager(Function, int, int)} and overriding the default maximum concurrency * value to be greater or equal to the expected number of groups, possibly using - * {@code Integer.MAX_VALUE} if the number of expected groups is unknown. + * {@link Integer#MAX_VALUE} if the number of expected groups is unknown. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Backpressure:
@@ -10558,9 +11355,8 @@ public final Flowable> groupBy(Function * the element type - * @return a {@code Publisher} that emits {@link GroupedFlowable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source Publisher that share that - * key value + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: GroupBy * @see #groupBy(Function, Function, boolean) * @see #groupBy(Function, Function, boolean, int) @@ -10569,36 +11365,37 @@ public final Flowable> groupBy(Function Flowable> groupBy(Function keySelector, - Function valueSelector) { + @NonNull + public final <@NonNull K, @NonNull V> Flowable> groupBy(@NonNull Function keySelector, + @NonNull Function valueSelector) { return groupBy(keySelector, valueSelector, false, bufferSize()); } /** - * Groups the items emitted by a {@code Publisher} according to a specified criterion, and emits these - * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedPublisher} allows only a single + * Groups the items emitted by the current {@code Flowable} according to a specified criterion, and emits these + * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedFlowable} allows only a single * {@link Subscriber} during its lifetime and if this {@code Subscriber} cancels before the * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedPublisher} emission. + * {@code GroupedFlowable} emission. *

- * + * *

- * Note: A {@link GroupedFlowable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedFlowable} 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 - * {@code GroupedPublisher}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedFlowable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

- * Note that the {@link GroupedFlowable}s should be subscribed to as soon as possible, otherwise, + * Note that the {@code GroupedFlowable}s should be subscribed to as soon as possible, otherwise, * the unconsumed groups may starve other groups due to the internal backpressure * coordination of the {@code groupBy} operator. Such hangs can be usually avoided by using * {@link #flatMap(Function, int)} or {@link #concatMapEager(Function, int, int)} and overriding the default maximum concurrency * value to be greater or equal to the expected number of groups, possibly using - * {@code Integer.MAX_VALUE} if the number of expected groups is unknown. + * {@link Integer#MAX_VALUE} if the number of expected groups is unknown. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Backpressure:
@@ -10623,47 +11420,47 @@ public final Flowable> groupBy(Function * the element type * @param delayError - * if true, the exception from the current Flowable is delayed in each group until that specific group emitted - * the normal values; if false, the exception bypasses values in the groups and is reported immediately. - * @return a {@code Publisher} that emits {@link GroupedFlowable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source Publisher that share that - * key value + * if {@code true}, the exception from the current {@code Flowable} is delayed in each group until that specific group emitted + * the normal values; if {@code false}, the exception bypasses values in the groups and is reported immediately. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: GroupBy * @see #groupBy(Function, Function, boolean, int) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> groupBy(Function keySelector, - Function valueSelector, boolean delayError) { + @NonNull + public final <@NonNull K, @NonNull V> Flowable> groupBy(@NonNull Function keySelector, + @NonNull Function valueSelector, boolean delayError) { return groupBy(keySelector, valueSelector, delayError, bufferSize()); } /** - * Groups the items emitted by a {@code Publisher} according to a specified criterion, and emits these - * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedPublisher} allows only a single + * Groups the items emitted by the current {@code Flowable} according to a specified criterion, and emits these + * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedFlowable} allows only a single * {@link Subscriber} during its lifetime and if this {@code Subscriber} cancels before the * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedPublisher} emission. + * {@code GroupedFlowable} emission. *

- * + * *

- * Note: A {@link GroupedFlowable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedFlowable} 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 - * {@code GroupedPublisher}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedFlowable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

- * Note that the {@link GroupedFlowable}s should be subscribed to as soon as possible, otherwise, + * Note that the {@code GroupedFlowable}s should be subscribed to as soon as possible, otherwise, * the unconsumed groups may starve other groups due to the internal backpressure * coordination of the {@code groupBy} operator. Such hangs can be usually avoided by using * {@link #flatMap(Function, int)} or {@link #concatMapEager(Function, int, int)} and overriding the default maximum concurrency * value to be greater or equal to the expected number of groups, possibly using - * {@code Integer.MAX_VALUE} if the number of expected groups is unknown. + * {@link Integer#MAX_VALUE} if the number of expected groups is unknown. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Backpressure:
@@ -10684,48 +11481,48 @@ public final Flowable> groupBy(Function * the key type * @param * the element type - * @return a {@code Publisher} that emits {@link GroupedFlowable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source Publisher that share that - * key value + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: GroupBy */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> groupBy(Function keySelector, - Function valueSelector, + public final <@NonNull K, @NonNull V> Flowable> groupBy(@NonNull Function keySelector, + @NonNull Function valueSelector, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableGroupBy(this, keySelector, valueSelector, bufferSize, delayError, null)); + return RxJavaPlugins.onAssembly(new FlowableGroupBy<>(this, keySelector, valueSelector, bufferSize, delayError, null)); } /** - * Groups the items emitted by a {@code Publisher} according to a specified criterion, and emits these + * Groups the items emitted by the current {@code Flowable} according to a specified criterion, and emits these * grouped items as {@link GroupedFlowable}s. The emitted {@code GroupedFlowable} allows only a single * {@link Subscriber} during its lifetime and if this {@code Subscriber} cancels before the * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedPublisher} emission. The {@code evictingMapFactory} is used to create a map that will - * be used to hold the {@link GroupedFlowable}s by key. The evicting map created by this factory must + * {@code GroupedFlowable} emission. The {@code evictingMapFactory} is used to create a map that will + * be used to hold the {@code GroupedFlowable}s by key. The evicting map created by this factory must * notify the provided {@code Consumer} with the entry value (not the key!) when an entry in this * map has been evicted. The next source emission will bring about the completion of the evicted - * {@link GroupedFlowable}s and the arrival of an item with the same key as a completed {@link GroupedFlowable} - * will prompt the creation and emission of a new {@link GroupedFlowable} with that key. + * {@code GroupedFlowable}s and the arrival of an item with the same key as a completed {@code GroupedFlowable} + * will prompt the creation and emission of a new {@code GroupedFlowable} with that key. * *

A use case for specifying an {@code evictingMapFactory} is where the source is infinite and fast and * over time the number of keys grows enough to be a concern in terms of the memory footprint of the - * internal hash map containing the {@link GroupedFlowable}s. + * internal hash map containing the {@code GroupedFlowable}s. * *

The map created by an {@code evictingMapFactory} must be thread-safe. * @@ -10750,7 +11547,7 @@ public final Flowable> groupBy(Function Flowable> groupBy(Function * *

- * + * *

- * Note: A {@link GroupedFlowable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedFlowable} 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 * {@code GroupedFlowable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

- * Note that the {@link GroupedFlowable}s should be subscribed to as soon as possible, otherwise, + * Note that the {@code GroupedFlowable}s should be subscribed to as soon as possible, otherwise, * the unconsumed groups may starve other groups due to the internal backpressure * coordination of the {@code groupBy} operator. Such hangs can be usually avoided by using * {@link #flatMap(Function, int)} or {@link #concatMapEager(Function, int, int)} and overriding the default maximum concurrency * value to be greater or equal to the expected number of groups, possibly using - * {@code Integer.MAX_VALUE} if the number of expected groups is unknown. + * {@link Integer#MAX_VALUE} if the number of expected groups is unknown. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Backpressure:
@@ -10797,23 +11594,23 @@ public final Flowable> groupBy(Function} with the entry value (not the key!) when * an entry in this map has been evicted. The next source emission will bring about the - * completion of the evicted {@link GroupedFlowable}s. See example above. + * completion of the evicted {@code GroupedFlowable}s. See example above. * @param * the key type * @param * the element type - * @return a {@code Publisher} that emits {@link GroupedFlowable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source Publisher that share that - * key value + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code keySelector}, {@code valueSelector} or {@code evictingMapFactory} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: GroupBy * * @since 2.2 @@ -10822,25 +11619,25 @@ public final Flowable> groupBy(Function Flowable> groupBy(Function keySelector, - Function valueSelector, + public final <@NonNull K, @NonNull V> Flowable> groupBy(@NonNull Function keySelector, + @NonNull Function valueSelector, boolean delayError, int bufferSize, - Function, ? extends Map> evictingMapFactory) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); + @NonNull Function, ? extends Map> evictingMapFactory) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(evictingMapFactory, "evictingMapFactory is null"); + Objects.requireNonNull(evictingMapFactory, "evictingMapFactory is null"); - return RxJavaPlugins.onAssembly(new FlowableGroupBy(this, keySelector, valueSelector, bufferSize, delayError, evictingMapFactory)); + return RxJavaPlugins.onAssembly(new FlowableGroupBy<>(this, keySelector, valueSelector, bufferSize, delayError, evictingMapFactory)); } /** - * Returns a Flowable that correlates two Publishers when they overlap in time and groups the results. + * Returns a {@code Flowable} that correlates two {@link Publisher}s when they overlap in time and groups the results. *

* There are no guarantees in what order the items get combined when multiple - * items from one or both source Publishers overlap. + * items from one or both source {@code Publisher}s overlap. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure and consumes all participating {@code Publisher}s in @@ -10849,44 +11646,44 @@ public final Flowable> groupBy(Function{@code groupJoin} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the right Publisher source - * @param the element type of the left duration Publishers - * @param the element type of the right duration Publishers + * @param the value type of the right {@code Publisher} source + * @param the element type of the left duration {@code Publisher}s + * @param the element type of the right duration {@code Publisher}s * @param the result type * @param other - * the other Publisher to correlate items from the source Publisher with + * the other {@code Publisher} to correlate items from the current {@code Flowable} with * @param leftEnd - * a function that returns a Publisher whose emissions indicate the duration of the values of - * the source Publisher + * a function that returns a {@code Publisher} whose emissions indicate the duration of the values of + * the current {@code Flowable} * @param rightEnd - * a function that returns a Publisher whose emissions indicate the duration of the values of - * the {@code right} Publisher + * a function that returns a {@code Publisher} whose emissions indicate the duration of the values of + * the {@code right} {@code Publisher} * @param resultSelector - * a function that takes an item emitted by each Publisher and returns the value to be emitted - * by the resulting Publisher - * @return a Flowable that emits items based on combining those items emitted by the source Publishers - * whose durations overlap + * a function that takes an item emitted by each {@code Publisher} and returns the value to be emitted + * by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other}, {@code leftEnd}, {@code rightEnd} or {@code resultSelector} is {@code null} * @see ReactiveX operators documentation: Join */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable groupJoin( - Publisher other, - Function> leftEnd, - Function> rightEnd, - BiFunction, ? extends R> resultSelector) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(leftEnd, "leftEnd is null"); - ObjectHelper.requireNonNull(rightEnd, "rightEnd is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); - return RxJavaPlugins.onAssembly(new FlowableGroupJoin( + public final <@NonNull TRight, @NonNull TLeftEnd, @NonNull TRightEnd, @NonNull R> Flowable groupJoin( + @NonNull Publisher other, + @NonNull Function> leftEnd, + @NonNull Function> rightEnd, + @NonNull BiFunction, ? extends R> resultSelector) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(leftEnd, "leftEnd is null"); + Objects.requireNonNull(rightEnd, "rightEnd is null"); + Objects.requireNonNull(resultSelector, "resultSelector is null"); + return RxJavaPlugins.onAssembly(new FlowableGroupJoin<>( this, other, leftEnd, rightEnd, resultSelector)); } /** - * Hides the identity of this Flowable and its Subscription. + * Hides the identity of this {@code Flowable} and its {@link Subscription}. *

Allows hiding extra features such as {@link Processor}'s * {@link Subscriber} methods or preventing certain identity-based * optimizations (fusion). @@ -10897,72 +11694,74 @@ public final Flowable groupJoin( *

Scheduler:
*
{@code hide} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Flowable instance + * @return the new {@code Flowable} instance * * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable hide() { - return RxJavaPlugins.onAssembly(new FlowableHide(this)); + return RxJavaPlugins.onAssembly(new FlowableHide<>(this)); } /** - * Ignores all items emitted by the source Publisher and only calls {@code onComplete} or {@code onError}. + * Ignores all items emitted by the current {@code Flowable} and only calls {@code onComplete} or {@code onError}. *

- * + * *

*
Backpressure:
- *
This operator ignores backpressure as it doesn't emit any elements and consumes the source {@code Publisher} + *
This operator ignores backpressure as it doesn't emit any elements and consumes the current {@code Flowable} * in an unbounded manner (i.e., no backpressure is applied to it).
*
Scheduler:
*
{@code ignoreElements} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Completable that only calls {@code onComplete} or {@code onError}, based on which one is - * called by the source Publisher + * @return the new {@link Completable} instance * @see ReactiveX operators documentation: IgnoreElements */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable ignoreElements() { - return RxJavaPlugins.onAssembly(new FlowableIgnoreElementsCompletable(this)); + return RxJavaPlugins.onAssembly(new FlowableIgnoreElementsCompletable<>(this)); } /** - * Returns a Single that emits {@code true} if the source Publisher is empty, otherwise {@code false}. + * Returns a {@link Single} that emits {@code true} if the current {@code Flowable} is empty, otherwise {@code false}. *

- * In Rx.Net this is negated as the {@code any} Subscriber but we renamed this in RxJava to better match Java + * In Rx.Net this is negated as the {@code any} {@link Subscriber} but we renamed this in RxJava to better match Java * naming idioms. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code isEmpty} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits a Boolean + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single isEmpty() { return all(Functions.alwaysFalse()); } /** - * Correlates the items emitted by two Publishers based on overlapping durations. + * Correlates the items emitted by two {@link Publisher}s based on overlapping durations. *

* There are no guarantees in what order the items get combined when multiple - * items from one or both source Publishers overlap. + * items from one or both source {@code Publisher}s overlap. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure and consumes all participating {@code Publisher}s in @@ -10971,113 +11770,116 @@ public final Single isEmpty() { *
{@code join} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the right Publisher source - * @param the element type of the left duration Publishers - * @param the element type of the right duration Publishers + * @param the value type of the right {@code Publisher} source + * @param the element type of the left duration {@code Publisher}s + * @param the element type of the right duration {@code Publisher}s * @param the result type * @param other - * the second Publisher to join items from + * the second {@code Publisher} to join items from * @param leftEnd - * a function to select a duration for each item emitted by the source Publisher, used to + * a function to select a duration for each item emitted by the current {@code Flowable}, used to * determine overlap * @param rightEnd - * a function to select a duration for each item emitted by the {@code right} Publisher, used to + * a function to select a duration for each item emitted by the {@code right} {@code Publisher}, used to * determine overlap * @param resultSelector - * a function that computes an item to be emitted by the resulting Publisher for any two - * overlapping items emitted by the two Publishers - * @return a Flowable that emits items correlating to items emitted by the source Publishers that have - * overlapping durations + * a function that computes an item to be emitted by the resulting {@code Flowable} for any two + * overlapping items emitted by the two {@code Publisher}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other}, {@code leftEnd}, {@code rightEnd} or {@code resultSelector} is {@code null} * @see ReactiveX operators documentation: Join */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable join( - Publisher other, - Function> leftEnd, - Function> rightEnd, - BiFunction resultSelector) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(leftEnd, "leftEnd is null"); - ObjectHelper.requireNonNull(rightEnd, "rightEnd is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); + public final <@NonNull TRight, @NonNull TLeftEnd, @NonNull TRightEnd, @NonNull R> Flowable join( + @NonNull Publisher other, + @NonNull Function> leftEnd, + @NonNull Function> rightEnd, + @NonNull BiFunction resultSelector) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(leftEnd, "leftEnd is null"); + Objects.requireNonNull(rightEnd, "rightEnd is null"); + Objects.requireNonNull(resultSelector, "resultSelector is null"); return RxJavaPlugins.onAssembly(new FlowableJoin( this, other, leftEnd, rightEnd, resultSelector)); } /** - * Returns a Maybe that emits the last item emitted by this Flowable or completes if - * this Flowable is empty. + * Returns a {@link Maybe} that emits the last item emitted by this {@code Flowable} or completes if + * this {@code Flowable} is empty. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code lastElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a new Maybe instance + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: Last */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe lastElement() { - return RxJavaPlugins.onAssembly(new FlowableLastMaybe(this)); + return RxJavaPlugins.onAssembly(new FlowableLastMaybe<>(this)); } /** - * Returns a Single that emits only the last item emitted by this Flowable, or a default item - * if this Flowable completes without emitting any items. + * Returns a {@link Single} that emits only the last item emitted by this {@code Flowable}, or a default item + * if this {@code Flowable} completes without emitting any items. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code last} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the default item to emit if the source Publisher is empty - * @return the new Single instance + * the default item to emit if the current {@code Flowable} is empty + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: Last */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single last(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem"); - return RxJavaPlugins.onAssembly(new FlowableLastSingle(this, defaultItem)); + public final Single last(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new FlowableLastSingle<>(this, defaultItem)); } /** - * Returns a Single that emits only the last item emitted by this Flowable or signals - * a {@link NoSuchElementException} if this Flowable is empty. + * Returns a {@link Single} that emits only the last item emitted by this {@code Flowable} or signals + * a {@link NoSuchElementException} if this {@code Flowable} is empty. *

* *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code lastOrError} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Single instance + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Last */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single lastOrError() { - return RxJavaPlugins.onAssembly(new FlowableLastSingle(this, null)); + return RxJavaPlugins.onAssembly(new FlowableLastSingle<>(this, null)); } /** @@ -11085,7 +11887,7 @@ public final Single lastOrError() { * other standard composition methods first; * Returns a {@code Flowable} which, when subscribed to, invokes the {@link FlowableOperator#apply(Subscriber) apply(Subscriber)} method * of the provided {@link FlowableOperator} for each individual downstream {@link Subscriber} and allows the - * insertion of a custom operator by accessing the downstream's {@link Subscriber} during this subscription phase + * insertion of a custom operator by accessing the downstream's {@code Subscriber} during this subscription phase * and providing a new {@code Subscriber}, containing the custom operator's intended business logic, that will be * used in the subscription process going further upstream. *

@@ -11202,27 +12004,28 @@ public final Single lastOrError() { * class and creating a {@link FlowableTransformer} with it is recommended. *

* Note also that it is not possible to stop the subscription phase in {@code lift()} as the {@code apply()} method - * requires a non-null {@code Subscriber} instance to be returned, which is then unconditionally subscribed to + * requires a non-{@code null} {@code Subscriber} instance to be returned, which is then unconditionally subscribed to * the upstream {@code Flowable}. For example, if the operator decided there is no reason to subscribe to the * upstream source because of some optimization possibility or a failure to prepare the operator, it still has to - * return a {@code Subscriber} that should immediately cancel the upstream's {@code Subscription} in its + * return a {@code Subscriber} that should immediately cancel the upstream's {@link Subscription} in its * {@code onSubscribe} method. Again, using a {@code FlowableTransformer} and extending the {@code Flowable} is * a better option as {@link #subscribeActual} can decide to not subscribe to its upstream after all. *

*
Backpressure:
- *
The {@code Subscriber} instance returned by the {@link FlowableOperator} is responsible to be - * backpressure-aware or document the fact that the consumer of the returned {@code Publisher} has to apply one of + *
The {@code Subscriber} instance returned by the {@code FlowableOperator} is responsible to be + * backpressure-aware or document the fact that the consumer of the returned {@link Publisher} has to apply one of * the {@code onBackpressureXXX} operators.
*
Scheduler:
*
{@code lift} does not operate by default on a particular {@link Scheduler}, however, the - * {@link FlowableOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
+ * {@code FlowableOperator} may use a {@code Scheduler} to support its own asynchronous behavior. *
* * @param the output value type - * @param lifter the {@link FlowableOperator} that receives the downstream's {@code Subscriber} and should return + * @param lifter the {@code FlowableOperator} that receives the downstream's {@code Subscriber} and should return * a {@code Subscriber} with custom behavior to be used as the consumer for the current * {@code Flowable}. - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code lifter} is {@code null} * @see RxJava wiki: Writing operators * @see #compose(FlowableTransformer) */ @@ -11230,19 +12033,19 @@ public final Single lastOrError() { @NonNull @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable lift(FlowableOperator lifter) { - ObjectHelper.requireNonNull(lifter, "lifter is null"); - return RxJavaPlugins.onAssembly(new FlowableLift(this, lifter)); + public final <@NonNull R> Flowable lift(@NonNull FlowableOperator lifter) { + Objects.requireNonNull(lifter, "lifter is null"); + return RxJavaPlugins.onAssembly(new FlowableLift<>(this, lifter)); } /** - * Returns a Flowable that applies a specified function to each item emitted by the source Publisher and + * Returns a {@code Flowable} that applies a specified function to each item emitted by the current {@code Flowable} and * emits the results of these function applications. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code map} does not operate by default on a particular {@link Scheduler}.
@@ -11250,78 +12053,80 @@ public final Flowable lift(FlowableOperator lifte * * @param the output type * @param mapper - * a function to apply to each item emitted by the Publisher - * @return a Flowable that emits the items from the source Publisher, transformed by the specified - * function + * a function to apply to each item emitted by the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: Map + * @see #mapOptional(Function) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable map(Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableMap(this, mapper)); + public final <@NonNull R> Flowable map(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableMap<>(this, mapper)); } /** - * Returns a Flowable that represents all of the emissions and notifications from the source - * Publisher into emissions marked with their original types within {@link Notification} objects. + * Returns a {@code Flowable} that represents all of the emissions and notifications from the current + * {@code Flowable} into emissions marked with their original types within {@link Notification} objects. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and expects it from the source {@code Publisher}. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors backpressure from downstream and expects it from the current {@code Flowable}. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits items that are the result of materializing the items and notifications - * of the source Publisher + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Materialize * @see #dematerialize(Function) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> materialize() { - return RxJavaPlugins.onAssembly(new FlowableMaterialize(this)); + return RxJavaPlugins.onAssembly(new FlowableMaterialize<>(this)); } /** - * Flattens this and another Publisher into a single Publisher, without any transformation. + * Flattens this and another {@link Publisher} into a single {@code Publisher}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Publishers so that they appear as a single Publisher, by + * You can combine items emitted by multiple {@code Publisher}s so that they appear as a single {@code Publisher}, by * using the {@code mergeWith} method. *

*
Backpressure:
*
The operator honors backpressure from downstream. This and the other {@code Publisher}s are expected to honor - * backpressure; if violated, the operator may signal {@code MissingBackpressureException}.
+ * backpressure; if violated, the operator may signal {@link MissingBackpressureException}. *
Scheduler:
*
{@code mergeWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * a Publisher to be merged - * @return a Flowable that emits all of the items emitted by the source Publishers + * a {@code Publisher} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Merge */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable mergeWith(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Flowable mergeWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); return merge(this, other); } /** - * Merges the sequence of items of this Flowable with the success value of the other SingleSource. + * Merges the sequence of items of this {@code Flowable} with the success value of the other {@link SingleSource}. *

- * + * *

* The success value of the other {@code SingleSource} can get interleaved at any point of this * {@code Flowable} sequence. @@ -11334,7 +12139,8 @@ public final Flowable mergeWith(Publisher other) { *

*

History: 2.1.10 - experimental * @param other the {@code SingleSource} whose success value to merge with - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @@ -11342,15 +12148,15 @@ public final Flowable mergeWith(Publisher other) { @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) public final Flowable mergeWith(@NonNull SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableMergeWithSingle(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableMergeWithSingle<>(this, other)); } /** - * Merges the sequence of items of this Flowable with the success value of the other MaybeSource - * or waits for both to complete normally if the MaybeSource is empty. + * Merges the sequence of items of this {@code Flowable} with the success value of the other {@link MaybeSource} + * or waits for both to complete normally if the {@code MaybeSource} is empty. *

- * + * *

* The success value of the other {@code MaybeSource} can get interleaved at any point of this * {@code Flowable} sequence. @@ -11363,7 +12169,8 @@ public final Flowable mergeWith(@NonNull SingleSource other) { * *

History: 2.1.10 - experimental * @param other the {@code MaybeSource} which provides a success value to merge with or completes - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @@ -11371,25 +12178,26 @@ public final Flowable mergeWith(@NonNull SingleSource other) { @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) public final Flowable mergeWith(@NonNull MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableMergeWithMaybe(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableMergeWithMaybe<>(this, other)); } /** - * Relays the items of this Flowable and completes only when the other CompletableSource completes + * Relays the items of this {@code Flowable} and completes only when the other {@link CompletableSource} completes * as well. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code mergeWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental * @param other the {@code CompletableSource} to await for completion - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @@ -11397,27 +12205,27 @@ public final Flowable mergeWith(@NonNull MaybeSource other) { @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) public final Flowable mergeWith(@NonNull CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableMergeWithCompletable(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableMergeWithCompletable<>(this, other)); } /** - * Modifies a Publisher to perform its emissions and notifications on a specified {@link Scheduler}, + * Signals the items and terminal signals of the current {@code Flowable} on the specified {@link Scheduler}, * asynchronously with a bounded buffer of {@link #bufferSize()} slots. * - *

Note that onError notifications will cut ahead of onNext notifications on the emission thread if Scheduler is truly + *

Note that {@code onError} notifications will cut ahead of {@code onNext} notifications on the emission thread if {@code Scheduler} is truly * asynchronous. If strict event ordering is required, consider using the {@link #observeOn(Scheduler, boolean)} overload. *

- * + * *

- * This operator keeps emitting as many signals as it can on the given Scheduler's Worker thread, + * This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s Worker thread, * which may result in a longer than expected occupation of this thread. In other terms, * it does not allow per-signal fairness in case the worker runs on a shared underlying thread. * If such fairness and signal/work interleaving is preferred, use the delay operator with zero time instead. *

*
Backpressure:
- *
This operator honors backpressure from downstream and expects it from the source {@code Publisher}. Violating this - * expectation will lead to {@code MissingBackpressureException}. This is the most common operator where the exception + *
This operator honors backpressure from downstream and expects it from the current {@code Flowable}. Violating this + * expectation will lead to {@link MissingBackpressureException}. This is the most common operator where the exception * pops up; look for sources up the chain that don't support backpressure, * such as {@link #interval(long, TimeUnit)}, {@link #timer(long, TimeUnit)}, * {@link io.reactivex.rxjava3.processors.PublishProcessor PublishProcessor} or @@ -11430,13 +12238,13 @@ public final Flowable mergeWith(@NonNull CompletableSource other) { * {@link #delay(long, TimeUnit, Scheduler)} with zero time instead. *
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to notify {@link Subscriber}s on - * @return the source Publisher modified so that its {@link Subscriber}s are notified on the specified - * {@link Scheduler} + * the {@code Scheduler} to notify {@link Subscriber}s on + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -11447,24 +12255,25 @@ public final Flowable mergeWith(@NonNull CompletableSource other) { @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable observeOn(Scheduler scheduler) { + @NonNull + public final Flowable observeOn(@NonNull Scheduler scheduler) { return observeOn(scheduler, false, bufferSize()); } /** - * Modifies a Publisher to perform its emissions and notifications on a specified {@link Scheduler}, - * asynchronously with a bounded buffer and optionally delays onError notifications. + * Signals the items and terminal signals of the current {@code Flowable} on the specified {@link Scheduler}, + * asynchronously with a bounded buffer and optionally delays {@code onError} notifications. *

- * + * *

- * This operator keeps emitting as many signals as it can on the given Scheduler's Worker thread, + * This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s Worker thread, * which may result in a longer than expected occupation of this thread. In other terms, * it does not allow per-signal fairness in case the worker runs on a shared underlying thread. * If such fairness and signal/work interleaving is preferred, use the delay operator with zero time instead. *

*
Backpressure:
- *
This operator honors backpressure from downstream and expects it from the source {@code Publisher}. Violating this - * expectation will lead to {@code MissingBackpressureException}. This is the most common operator where the exception + *
This operator honors backpressure from downstream and expects it from the current {@code Flowable}. Violating this + * expectation will lead to {@link MissingBackpressureException}. This is the most common operator where the exception * pops up; look for sources up the chain that don't support backpressure, * such as {@link #interval(long, TimeUnit)}, {@link #timer(long, TimeUnit)}, * {@link io.reactivex.rxjava3.processors.PublishProcessor PublishProcessor} or @@ -11477,17 +12286,17 @@ public final Flowable observeOn(Scheduler scheduler) { * {@link #delay(long, TimeUnit, Scheduler, boolean)} with zero time instead. *
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to notify {@link Subscriber}s on + * the {@code Scheduler} to notify {@link Subscriber}s on * @param delayError - * indicates if the onError notification may not cut ahead of onNext notification on the other side of the - * scheduling boundary. If true a sequence ending in onError will be replayed in the same order as was received + * indicates if the {@code onError} notification may not cut ahead of {@code onNext} notification on the other side of the + * scheduling boundary. If {@code true}, a sequence ending in {@code onError} will be replayed in the same order as was received * from upstream - * @return the source Publisher modified so that its {@link Subscriber}s are notified on the specified - * {@link Scheduler} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -11498,24 +12307,25 @@ public final Flowable observeOn(Scheduler scheduler) { @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable observeOn(Scheduler scheduler, boolean delayError) { + @NonNull + public final Flowable observeOn(@NonNull Scheduler scheduler, boolean delayError) { return observeOn(scheduler, delayError, bufferSize()); } /** - * Modifies a Publisher to perform its emissions and notifications on a specified {@link Scheduler}, - * asynchronously with a bounded buffer of configurable size and optionally delays onError notifications. + * Signals the items and terminal signals of the current {@code Flowable} on the specified {@link Scheduler}, + * asynchronously with a bounded buffer of configurable size and optionally delays {@code onError} notifications. *

- * + * *

- * This operator keeps emitting as many signals as it can on the given Scheduler's Worker thread, + * This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s Worker thread, * which may result in a longer than expected occupation of this thread. In other terms, * it does not allow per-signal fairness in case the worker runs on a shared underlying thread. * If such fairness and signal/work interleaving is preferred, use the delay operator with zero time instead. *

*
Backpressure:
- *
This operator honors backpressure from downstream and expects it from the source {@code Publisher}. Violating this - * expectation will lead to {@code MissingBackpressureException}. This is the most common operator where the exception + *
This operator honors backpressure from downstream and expects it from the current {@code Flowable}. Violating this + * expectation will lead to {@link MissingBackpressureException}. This is the most common operator where the exception * pops up; look for sources up the chain that don't support backpressure, * such as {@link #interval(long, TimeUnit)}, {@link #timer(long, TimeUnit)}, * {@link io.reactivex.rxjava3.processors.PublishProcessor PublishProcessor} or @@ -11528,18 +12338,19 @@ public final Flowable observeOn(Scheduler scheduler, boolean delayError) { * {@link #delay(long, TimeUnit, Scheduler, boolean)} with zero time instead. *
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to notify {@link Subscriber}s on + * the {@code Scheduler} to notify {@link Subscriber}s on * @param delayError - * indicates if the onError notification may not cut ahead of onNext notification on the other side of the - * scheduling boundary. If true a sequence ending in onError will be replayed in the same order as was received + * indicates if the {@code onError} notification may not cut ahead of {@code onNext} notification on the other side of the + * scheduling boundary. If {@code true}, a sequence ending in {@code onError} will be replayed in the same order as was received * from upstream * @param bufferSize the size of the buffer. - * @return the source Publisher modified so that its {@link Subscriber}s are notified on the specified - * {@link Scheduler} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -11551,19 +12362,19 @@ public final Flowable observeOn(Scheduler scheduler, boolean delayError) { @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable observeOn(Scheduler scheduler, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Flowable observeOn(@NonNull Scheduler scheduler, boolean delayError, int bufferSize) { + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableObserveOn(this, scheduler, delayError, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableObserveOn<>(this, scheduler, delayError, bufferSize)); } /** - * Filters the items emitted by a Publisher, only emitting those of the specified type. + * Filters the items emitted by the current {@code Flowable}, only emitting those of the specified type. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code ofType} does not operate by default on a particular {@link Scheduler}.
@@ -11571,105 +12382,118 @@ public final Flowable observeOn(Scheduler scheduler, boolean delayError, int * * @param the output type * @param clazz - * the class type to filter the items emitted by the source Publisher - * @return a Flowable that emits items from the source Publisher of type {@code clazz} + * the class type to filter the items emitted by the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code clazz} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable ofType(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + public final <@NonNull U> Flowable ofType(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return filter(Functions.isInstanceOf(clazz)).cast(clazz); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer these - * items indefinitely until they can be emitted. + * Buffers an unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place. + *

+ * *

- * + * An error from the current {@code Flowable} will cut ahead of any unconsumed item. Use {@link #onBackpressureBuffer(boolean)} + * to have the operator keep the original signal order. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source Publisher modified to buffer items to the extent system resources allow + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: backpressure operators + * @see #onBackpressureBuffer(boolean) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureBuffer() { return onBackpressureBuffer(bufferSize(), false, true); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer these - * items indefinitely until they can be emitted. + * Buffers an unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place, optionally delaying an error until all buffered items have been consumed. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
*
* @param delayError - * if true, an exception from the current Flowable is delayed until all buffered elements have been - * consumed by the downstream; if false, an exception is immediately signaled to the downstream, skipping + * if {@code true}, an exception from the current {@code Flowable} is delayed until all buffered elements have been + * consumed by the downstream; if {@code false}, an exception is immediately signaled to the downstream, skipping * any buffered element - * @return the source Publisher modified to buffer items to the extent system resources allow + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: backpressure operators */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureBuffer(boolean delayError) { return onBackpressureBuffer(bufferSize(), delayError, true); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer up to - * a given amount of items until they can be emitted. The resulting Publisher will signal - * a {@code BufferOverflowException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered - * items, and canceling the source. + * Buffers an limited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place, however, the resulting {@code Flowable} will signal a + * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered + * items, and canceling the flow. *

- * + * + *

+ * An error from the current {@code Flowable} will cut ahead of any unconsumed item. Use {@link #onBackpressureBuffer(int, boolean)} + * to have the operator keep the original signal order. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
*
* * @param capacity number of slots available in the buffer. - * @return the source {@code Publisher} modified to buffer items up to the given capacity. + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code capacity} is non-positive * @see ReactiveX operators documentation: backpressure operators * @since 1.1.0 + * @see #onBackpressureBuffer(long, Action, BackpressureOverflowStrategy) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureBuffer(int capacity) { return onBackpressureBuffer(capacity, false, false); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer up to - * a given amount of items until they can be emitted. The resulting Publisher will signal - * a {@code BufferOverflowException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered - * items, and canceling the source. + * Buffers an limited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place, however, the resulting {@code Flowable} will signal a + * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered + * items, and canceling the flow. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
@@ -11677,30 +12501,33 @@ public final Flowable onBackpressureBuffer(int capacity) { * * @param capacity number of slots available in the buffer. * @param delayError - * if true, an exception from the current Flowable is delayed until all buffered elements have been - * consumed by the downstream; if false, an exception is immediately signaled to the downstream, skipping + * if {@code true}, an exception from the current {@code Flowable} is delayed until all buffered elements have been + * consumed by the downstream; if {@code false}, an exception is immediately signaled to the downstream, skipping * any buffered element - * @return the source {@code Publisher} modified to buffer items up to the given capacity. + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code capacity} is non-positive * @see ReactiveX operators documentation: backpressure operators * @since 1.1.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureBuffer(int capacity, boolean delayError) { return onBackpressureBuffer(capacity, delayError, false); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer up to - * a given amount of items until they can be emitted. The resulting Publisher will signal - * a {@code BufferOverflowException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered - * items, and canceling the source. + * Buffers an optionally unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place. + * If {@code unbounded} is {@code true}, the resulting {@code Flowable} will signal a + * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered + * items, and canceling the flow. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
@@ -11708,33 +12535,36 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError) * * @param capacity number of slots available in the buffer. * @param delayError - * if true, an exception from the current Flowable is delayed until all buffered elements have been - * consumed by the downstream; if false, an exception is immediately signaled to the downstream, skipping + * if {@code true}, an exception from the current {@code Flowable} is delayed until all buffered elements have been + * consumed by the downstream; if {@code false}, an exception is immediately signaled to the downstream, skipping * any buffered element * @param unbounded - * if true, the capacity value is interpreted as the internal "island" size of the unbounded buffer - * @return the source {@code Publisher} modified to buffer items up to the given capacity. + * if {@code true}, the capacity value is interpreted as the internal "island" size of the unbounded buffer + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code capacity} is non-positive * @see ReactiveX operators documentation: backpressure operators * @since 1.1.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureBuffer(int capacity, boolean delayError, boolean unbounded) { ObjectHelper.verifyPositive(capacity, "capacity"); - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBuffer(this, capacity, unbounded, delayError, Functions.EMPTY_ACTION)); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBuffer<>(this, capacity, unbounded, delayError, Functions.EMPTY_ACTION, Functions.emptyConsumer())); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer up to - * a given amount of items until they can be emitted. The resulting Publisher will signal - * a {@code BufferOverflowException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered - * items, canceling the source, and notifying the producer with {@code onOverflow}. + * Buffers an optionally unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place. + * If {@code unbounded} is {@code true}, the resulting {@code Flowable} will signal a + * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered + * items, canceling the flow and calling the {@code onOverflow} action. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
@@ -11742,14 +12572,17 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError, * * @param capacity number of slots available in the buffer. * @param delayError - * if true, an exception from the current Flowable is delayed until all buffered elements have been - * consumed by the downstream; if false, an exception is immediately signaled to the downstream, skipping + * if {@code true}, an exception from the current {@code Flowable} is delayed until all buffered elements have been + * consumed by the downstream; if {@code false}, an exception is immediately signaled to the downstream, skipping * any buffered element * @param unbounded - * if true, the capacity value is interpreted as the internal "island" size of the unbounded buffer - * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots. Null is allowed. - * @return the source {@code Publisher} modified to buffer items up to the given capacity + * if {@code true}, the capacity value is interpreted as the internal "island" size of the unbounded buffer + * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onOverflow} is {@code null} + * @throws IllegalArgumentException if {@code capacity} is non-positive * @see ReactiveX operators documentation: backpressure operators + * @see #onBackpressureBuffer(int, boolean, boolean, Action, Consumer) * @since 1.1.0 */ @CheckReturnValue @@ -11757,22 +12590,66 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError, @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) public final Flowable onBackpressureBuffer(int capacity, boolean delayError, boolean unbounded, - Action onOverflow) { - ObjectHelper.requireNonNull(onOverflow, "onOverflow is null"); + @NonNull Action onOverflow) { + Objects.requireNonNull(onOverflow, "onOverflow is null"); + ObjectHelper.verifyPositive(capacity, "capacity"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBuffer<>(this, capacity, unbounded, delayError, onOverflow, Functions.emptyConsumer())); + } + + /** + * Buffers an optionally unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place. + * If {@code unbounded} is {@code true}, the resulting {@code Flowable} will signal a + * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered + * items, canceling the flow and calling the {@code onOverflow} action. + *

+ * + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param capacity number of slots available in the buffer. + * @param delayError + * if {@code true}, an exception from the current {@code Flowable} is delayed until all buffered elements have been + * consumed by the downstream; if {@code false}, an exception is immediately signaled to the downstream, skipping + * any buffered element + * @param unbounded + * if {@code true}, the capacity value is interpreted as the internal "island" size of the unbounded buffer + * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots. + * @param onDropped the {@link Consumer} to be called with the item that could not be buffered due to capacity constraints. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onOverflow} or {@code onDropped} is {@code null} + * @throws IllegalArgumentException if {@code capacity} is non-positive + * @see ReactiveX operators documentation: backpressure operators + * @since 3.1.7 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.SPECIAL) + @SchedulerSupport(SchedulerSupport.NONE) + @Experimental + public final Flowable onBackpressureBuffer(int capacity, boolean delayError, boolean unbounded, + @NonNull Action onOverflow, @NonNull Consumer onDropped) { + Objects.requireNonNull(onOverflow, "onOverflow is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); ObjectHelper.verifyPositive(capacity, "capacity"); - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBuffer(this, capacity, unbounded, delayError, onOverflow)); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBuffer<>(this, capacity, unbounded, delayError, onOverflow, onDropped)); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer up to - * a given amount of items until they can be emitted. The resulting Publisher will signal - * a {@code BufferOverflowException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered - * items, canceling the source, and notifying the producer with {@code onOverflow}. + * Buffers an limited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place, however, the resulting {@code Flowable} will signal a + * {@link MissingBackpressureException} via {@code onError} as soon as the buffer's capacity is exceeded, dropping all undelivered + * items, canceling the flow and calling the {@code onOverflow} action. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
@@ -11780,104 +12657,158 @@ public final Flowable onBackpressureBuffer(int capacity, boolean delayError, * * @param capacity number of slots available in the buffer. * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots. Null is allowed. - * @return the source {@code Publisher} modified to buffer items up to the given capacity + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onOverflow} is {@code null} + * @throws IllegalArgumentException if {@code capacity} is non-positive * @see ReactiveX operators documentation: backpressure operators * @since 1.1.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onBackpressureBuffer(int capacity, Action onOverflow) { + @NonNull + public final Flowable onBackpressureBuffer(int capacity, @NonNull Action onOverflow) { return onBackpressureBuffer(capacity, false, false, onOverflow); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to buffer up to - * a given amount of items until they can be emitted. The resulting Publisher will behave as determined - * by {@code overflowStrategy} if the buffer capacity is exceeded. - * + * Buffers an optionally unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place. + * The resulting {@code Flowable} will behave as determined by {@code overflowStrategy} if the buffer capacity is exceeded: *
    - *
  • {@code BackpressureOverflow.Strategy.ON_OVERFLOW_ERROR} (default) will call {@code onError} dropping all undelivered items, + *
  • {@link BackpressureOverflowStrategy#ERROR} (default) will call {@code onError} dropping all undelivered items, * canceling the source, and notifying the producer with {@code onOverflow}.
  • - *
  • {@code BackpressureOverflow.Strategy.ON_OVERFLOW_DROP_LATEST} will drop any new items emitted by the producer while + *
  • {@link BackpressureOverflowStrategy#DROP_LATEST} will drop any new items emitted by the producer while * the buffer is full, without generating any {@code onError}. Each drop will, however, invoke {@code onOverflow} * to signal the overflow to the producer.
  • - *
  • {@code BackpressureOverflow.Strategy.ON_OVERFLOW_DROP_OLDEST} will drop the oldest items in the buffer in order to make - * room for newly emitted ones. Overflow will not generate an{@code onError}, but each drop will invoke + *
  • {@link BackpressureOverflowStrategy#DROP_OLDEST} will drop the oldest items in the buffer in order to make + * room for newly emitted ones. Overflow will not generate an {@code onError}, but each drop will invoke * {@code onOverflow} to signal the overflow to the producer.
  • *
* *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
*
* * @param capacity number of slots available in the buffer. - * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots. Null is allowed. - * @param overflowStrategy how should the {@code Publisher} react to buffer overflows. Null is not allowed. - * @return the source {@code Flowable} modified to buffer items up to the given capacity + * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots, {@code null} is allowed. + * @param overflowStrategy how should the resulting {@code Flowable} react to buffer overflows, {@code null} is not allowed. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onOverflow} or {@code overflowStrategy} is {@code null} + * @throws IllegalArgumentException if {@code capacity} is non-positive * @see ReactiveX operators documentation: backpressure operators + * @see #onBackpressureBuffer(long, Action, BackpressureOverflowStrategy) * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onBackpressureBuffer(long capacity, Action onOverflow, BackpressureOverflowStrategy overflowStrategy) { - ObjectHelper.requireNonNull(overflowStrategy, "overflowStrategy is null"); + public final Flowable onBackpressureBuffer(long capacity, @Nullable Action onOverflow, @NonNull BackpressureOverflowStrategy overflowStrategy) { + Objects.requireNonNull(overflowStrategy, "overflowStrategy is null"); ObjectHelper.verifyPositive(capacity, "capacity"); - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBufferStrategy(this, capacity, onOverflow, overflowStrategy)); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBufferStrategy<>(this, capacity, onOverflow, overflowStrategy, null)); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to discard, - * rather than emit, those items that its Subscriber is not prepared to observe. + * Buffers an optionally unlimited number of items from the current {@code Flowable} and allows it to emit as fast it can while allowing the + * downstream to consume the items at its own place. + * The resulting {@code Flowable} will behave as determined by {@code overflowStrategy} if the buffer capacity is exceeded: + *
    + *
  • {@link BackpressureOverflowStrategy#ERROR} (default) will call {@code onError} dropping all undelivered items, + * canceling the source, and notifying the producer with {@code onOverflow}.
  • + *
  • {@link BackpressureOverflowStrategy#DROP_LATEST} will drop any new items emitted by the producer while + * the buffer is full, without generating any {@code onError}. Each drop will, however, invoke {@code onOverflow} + * to signal the overflow to the producer.
  • + *
  • {@link BackpressureOverflowStrategy#DROP_OLDEST} will drop the oldest items in the buffer in order to make + * room for newly emitted ones. Overflow will not generate an {@code onError}, but each drop will invoke + * {@code onOverflow} to signal the overflow to the producer.
  • + *
+ * + *

+ * + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureBuffer} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param capacity number of slots available in the buffer. + * @param onOverflow action to execute if an item needs to be buffered, but there are no available slots, {@code null} is allowed. + * @param overflowStrategy how should the resulting {@code Flowable} react to buffer overflows, {@code null} is not allowed. + * @param onDropped the {@link Consumer} to be called with the item that could not be buffered due to capacity constraints. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onOverflow}, {@code overflowStrategy} or {@code onDropped} is {@code null} + * @throws IllegalArgumentException if {@code capacity} is non-positive + * @see ReactiveX operators documentation: backpressure operators + * @since 3.1.7 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.SPECIAL) + @SchedulerSupport(SchedulerSupport.NONE) + @Experimental + public final Flowable onBackpressureBuffer(long capacity, @Nullable Action onOverflow, @NonNull BackpressureOverflowStrategy overflowStrategy, @NonNull Consumer onDropped) { + Objects.requireNonNull(overflowStrategy, "overflowStrategy is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + ObjectHelper.verifyPositive(capacity, "capacity"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureBufferStrategy<>(this, capacity, onOverflow, overflowStrategy, onDropped)); + } + /** + * Drops items from the current {@code Flowable} if the downstream is not ready to receive new items (indicated + * by a lack of {@link Subscription#request(long)} calls from it). *

- * + * *

- * If the downstream request count hits 0 then the Publisher will refrain from calling {@code onNext} until - * the Subscriber invokes {@code request(n)} again to increase the request count. + * If the downstream request count hits 0 then the resulting {@code Flowable} will refrain from calling {@code onNext} until + * the {@link Subscriber} invokes {@code request(n)} again to increase the request count. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureDrop} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source Publisher modified to drop {@code onNext} notifications on overflow + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: backpressure operators */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureDrop() { - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureDrop(this)); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureDrop<>(this)); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to discard, - * rather than emit, those items that its Subscriber is not prepared to observe. + * Drops items from the current {@code Flowable} if the downstream is not ready to receive new items (indicated + * by a lack of {@link Subscription#request(long)} calls from it) and calls the given {@link Consumer} with such + * dropped items. *

- * + * *

- * If the downstream request count hits 0 then the Publisher will refrain from calling {@code onNext} until - * the Subscriber invokes {@code request(n)} again to increase the request count. + * If the downstream request count hits 0 then the resulting {@code Flowable} will refrain from calling {@code onNext} until + * the {@link Subscriber} invokes {@code request(n)} again to increase the request count. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureDrop} does not operate by default on a particular {@link Scheduler}.
*
* - * @param onDrop the action to invoke for each item dropped. onDrop action should be fast and should never block. - * @return the source Publisher modified to drop {@code onNext} notifications on overflow + * @param onDrop the action to invoke for each item dropped, should be fast and should never block. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code onDrop} is {@code null} * @see ReactiveX operators documentation: backpressure operators * @since 1.1.0 */ @@ -11885,57 +12816,230 @@ public final Flowable onBackpressureDrop() { @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onBackpressureDrop(Consumer onDrop) { - ObjectHelper.requireNonNull(onDrop, "onDrop is null"); - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureDrop(this, onDrop)); + public final Flowable onBackpressureDrop(@NonNull Consumer onDrop) { + Objects.requireNonNull(onDrop, "onDrop is null"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureDrop<>(this, onDrop)); } /** - * Instructs a Publisher that is emitting items faster than its Subscriber can consume them to - * hold onto the latest value and emit that on request. + * Drops all but the latest item emitted by the current {@code Flowable} if the downstream is not ready to receive + * new items (indicated by a lack of {@link Subscription#request(long)} calls from it) and emits this latest + * item when the downstream becomes ready. *

- * + * *

* Its behavior is logically equivalent to {@code blockingLatest()} with the exception that * the downstream is not blocking while requesting more values. *

- * Note that if the upstream Publisher does support backpressure, this operator ignores that capability + * Note that if the current {@code Flowable} does support backpressure, this operator ignores that capability * and doesn't propagate any backpressure requests from downstream. *

* Note that due to the nature of how backpressure requests are propagated through subscribeOn/observeOn, - * requesting more than 1 from downstream doesn't guarantee a continuous delivery of onNext events. + * requesting more than 1 from downstream doesn't guarantee a continuous delivery of {@code onNext} events. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an unbounded + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded * manner (i.e., not applying backpressure to it).
*
Scheduler:
*
{@code onBackpressureLatest} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source Publisher modified so that it emits the most recently-received item upon request + * @return the new {@code Flowable} instance * @since 1.1.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onBackpressureLatest() { - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureLatest(this)); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureLatest<>(this, null)); + } + + /** + * Drops all but the latest item emitted by the current {@code Flowable} if the downstream is not ready to receive + * new items (indicated by a lack of {@link Subscription#request(long)} calls from it) and emits this latest + * item when the downstream becomes ready. + *

+ * + *

+ * Its behavior is logically equivalent to {@code blockingLatest()} with the exception that + * the downstream is not blocking while requesting more values. + *

+ * Note that if the current {@code Flowable} does support backpressure, this operator ignores that capability + * and doesn't propagate any backpressure requests from downstream. + *

+ * Note that due to the nature of how backpressure requests are propagated through subscribeOn/observeOn, + * requesting more than 1 from downstream doesn't guarantee a continuous delivery of {@code onNext} events. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureLatest} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @throws NullPointerException if {@code onDropped} is {@code null} + * @return the new {@code Flowable} instance + * @since 3.1.7 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + @Experimental + public final Flowable onBackpressureLatest(@NonNull Consumer onDropped) { + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureLatest<>(this, onDropped)); + } + + /** + * Reduces a sequence of two not emitted values via a function into a single value if the downstream is not ready to receive + * new items (indicated by a lack of {@link Subscription#request(long)} calls from it) and emits this latest + * item when the downstream becomes ready. + *

+ * + *

+ * Note that if the current {@code Flowable} does support backpressure, this operator ignores that capability + * and doesn't propagate any backpressure requests from downstream. + *

+ * Note that due to the nature of how backpressure requests are propagated through subscribeOn/observeOn, + * requesting more than 1 from downstream doesn't guarantee a continuous delivery of {@code onNext} events. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 3.0.9 - experimental + * @param reducer the bi-function to call when there is more than one non-emitted value to downstream, + * the first argument of the bi-function is previous item and the second one is currently + * emitting from upstream + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code reducer} is {@code null} + * @since 3.1.0 + * @see #onBackpressureReduce(Supplier, BiFunction) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Flowable onBackpressureReduce(@NonNull BiFunction reducer) { + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureReduce<>(this, reducer)); + } + + /** + * Reduces upstream values into an aggregate value, provided by a supplier and combined via a reducer function, + * while the downstream is not ready to receive items, then emits this aggregate value when the downstream becomes ready. + *

+ * + *

+ * Note that even if the downstream is ready to receive an item, the upstream item will always be aggregated into the output type, + * calling both the supplier and the reducer to produce the output value. + *

+ * Note that if the current {@code Flowable} does support backpressure, this operator ignores that capability + * and doesn't propagate any backpressure requests from downstream. + *

+ * Note that due to the nature of how backpressure requests are propagated through subscribeOn/observeOn, + * requesting more than 1 from downstream doesn't guarantee a continuous delivery of {@code onNext} events. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an unbounded + * manner (i.e., not applying backpressure to it).
+ *
Scheduler:
+ *
{@code onBackpressureReduce} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 3.0.9 - experimental + * @param the aggregate type emitted when the downstream requests more items + * @param supplier the factory to call to create new item of type R to pass it as the first argument to {@code reducer}. + * It is called when previous returned value by {@code reducer} already sent to + * downstream or the very first update from upstream received. + * @param reducer the bi-function to call to reduce excessive updates which downstream is not ready to receive. + * The first argument of type R is the object returned by {@code supplier} or result of previous + * {@code reducer} invocation. The second argument of type T is the current update from upstream. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code supplier} or {@code reducer} is {@code null} + * @see #onBackpressureReduce(BiFunction) + * @since 3.1.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Flowable onBackpressureReduce(@NonNull Supplier supplier, @NonNull BiFunction reducer) { + Objects.requireNonNull(supplier, "supplier is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureReduceWith<>(this, supplier, reducer)); + } + + /** + * Returns a {@code Flowable} instance that if the current {@code Flowable} emits an error, it will emit an {@code onComplete} + * and swallow the throwable. + *

+ * + *

+ *
Backpressure:
+ *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure + * behavior.
+ *
Scheduler:
+ *
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code Flowable} instance + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @NonNull + public final Flowable onErrorComplete() { + return onErrorComplete(Functions.alwaysTrue()); + } + + /** + * Returns a {@code Flowable} instance that if the current {@code Flowable} emits an error and the predicate returns + * {@code true}, it will emit an {@code onComplete} and swallow the throwable. + *

+ * + *

+ *
Backpressure:
+ *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure + * behavior.
+ *
Scheduler:
+ *
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param predicate the predicate to call when an {@link Throwable} is emitted which should return {@code true} + * if the {@code Throwable} should be swallowed and replaced with an {@code onComplete}. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Flowable onErrorComplete(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + + return RxJavaPlugins.onAssembly(new FlowableOnErrorComplete<>(this, predicate)); } /** - * Instructs a Publisher to pass control to another Publisher rather than invoking - * {@link Subscriber#onError onError} if it encounters an error. + * Resumes the flow with a {@link Publisher} returned for the failure {@link Throwable} of the current {@code Flowable} by a + * function instead of signaling the error via {@code onError}. *

- * + * *

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

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -11945,41 +13049,42 @@ public final Flowable onBackpressureLatest() { *

The operator honors backpressure from downstream. This and the resuming {@code Publisher}s * are expected to honor backpressure as well. * If any of them violate this expectation, the operator may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes or - * a {@code MissingBackpressureException} is signaled somewhere downstream.
+ * {@link IllegalStateException} when the current {@code Flowable} completes or + * a {@link MissingBackpressureException} is signaled somewhere downstream. *
Scheduler:
*
{@code onErrorResumeNext} does not operate by default on a particular {@link Scheduler}.
*
* - * @param resumeFunction - * a function that returns a Publisher that will take over if the source Publisher encounters + * @param fallbackSupplier + * a function that returns a {@code Publisher} that will take over if the current {@code Flowable} encounters * an error - * @return the original Publisher, with appropriately modified behavior + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code fallbackSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onErrorResumeNext(Function> resumeFunction) { - ObjectHelper.requireNonNull(resumeFunction, "resumeFunction is null"); - return RxJavaPlugins.onAssembly(new FlowableOnErrorNext(this, resumeFunction)); + public final Flowable onErrorResumeNext(@NonNull Function> fallbackSupplier) { + Objects.requireNonNull(fallbackSupplier, "fallbackSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableOnErrorNext<>(this, fallbackSupplier)); } /** - * Instructs a Publisher to pass control to another Publisher rather than invoking - * {@link Subscriber#onError onError} if it encounters an error. + * Resumes the flow with the given {@link Publisher} when the current {@code Flowable} fails instead of + * signaling the error via {@code onError}. *

- * + * *

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

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -11989,126 +13094,129 @@ public final Flowable onErrorResumeNext(FunctionThe operator honors backpressure from downstream. This and the resuming {@code Publisher}s * are expected to honor backpressure as well. * If any of them violate this expectation, the operator may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes or - * {@code MissingBackpressureException} is signaled somewhere downstream. + * {@link IllegalStateException} when the current {@code Flowable} completes or + * {@link MissingBackpressureException} is signaled somewhere downstream. *

Scheduler:
*
{@code onErrorResumeWith} does not operate by default on a particular {@link Scheduler}.
*
* - * @param next - * the next Publisher source that will take over if the source Publisher encounters + * @param fallback + * the next {@code Publisher} source that will take over if the current {@code Flowable} encounters * an error - * @return the original Publisher, with appropriately modified behavior + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code fallback} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onErrorResumeWith(final Publisher next) { - ObjectHelper.requireNonNull(next, "next is null"); - return onErrorResumeNext(Functions.justFunction(next)); + public final Flowable onErrorResumeWith(@NonNull Publisher fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return onErrorResumeNext(Functions.justFunction(fallback)); } /** - * Instructs a Publisher to emit an item (returned by a specified function) rather than invoking - * {@link Subscriber#onError onError} if it encounters an error. + * Ends the flow with a last item returned by a function for the {@link Throwable} error signaled by the current + * {@code Flowable} instead of signaling the error via {@code onError}. *

- * + * *

- * By default, when a Publisher encounters an error that prevents it from emitting the expected item to - * its {@link Subscriber}, the Publisher invokes its Subscriber's {@code onError} method, and then quits - * without invoking any more of its Subscriber's methods. The {@code onErrorReturn} method changes this - * behavior. If you pass a function ({@code resumeFunction}) to a Publisher's {@code onErrorReturn} - * method, if the original Publisher encounters an error, instead of invoking its Subscriber's + * By default, when a {@link Publisher} encounters an error that prevents it from emitting the expected item to + * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits + * without invoking any more of its {@code Subscriber}'s methods. The {@code onErrorReturn} method changes this + * behavior. If you pass a function ({@code resumeFunction}) to a {@code Publisher}'s {@code onErrorReturn} + * method, if the original {@code Publisher} encounters an error, instead of invoking its {@code Subscriber}'s * {@code onError} method, it will instead emit the return value of {@code resumeFunction}. *

* You can use this to prevent errors from propagating or to supply fallback data should errors be * encountered. *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is expected to honor + *
The operator honors backpressure from downstream. The current {@code Flowable} is expected to honor * backpressure as well. If it this expectation is violated, the operator may throw - * {@code IllegalStateException} when the source {@code Publisher} completes or - * {@code MissingBackpressureException} is signaled somewhere downstream.
+ * {@link IllegalStateException} when the current {@code Flowable} completes or + * {@link MissingBackpressureException} is signaled somewhere downstream. *
Scheduler:
*
{@code onErrorReturn} does not operate by default on a particular {@link Scheduler}.
*
* - * @param valueSupplier - * a function that returns a single value that will be emitted along with a regular onComplete in case - * the current Flowable signals an onError event - * @return the original Publisher with appropriately modified behavior + * @param itemSupplier + * a function that returns a single value that will be emitted along with a regular {@code onComplete} in case + * the current {@code Flowable} signals an {@code onError} event + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code itemSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onErrorReturn(Function valueSupplier) { - ObjectHelper.requireNonNull(valueSupplier, "valueSupplier is null"); - return RxJavaPlugins.onAssembly(new FlowableOnErrorReturn(this, valueSupplier)); + public final Flowable onErrorReturn(@NonNull Function itemSupplier) { + Objects.requireNonNull(itemSupplier, "itemSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableOnErrorReturn<>(this, itemSupplier)); } /** - * Instructs a Publisher to emit an item (returned by a specified function) rather than invoking - * {@link Subscriber#onError onError} if it encounters an error. + * Ends the flow with the given last item when the current {@code Flowable} fails instead of signaling the error via {@code onError}. *

- * + * *

- * By default, when a Publisher encounters an error that prevents it from emitting the expected item to - * its {@link Subscriber}, the Publisher invokes its Subscriber's {@code onError} method, and then quits - * without invoking any more of its Subscriber's methods. The {@code onErrorReturn} method changes this - * behavior. If you pass a function ({@code resumeFunction}) to a Publisher's {@code onErrorReturn} - * method, if the original Publisher encounters an error, instead of invoking its Subscriber's + * By default, when a {@link Publisher} encounters an error that prevents it from emitting the expected item to + * its {@link Subscriber}, the {@code Publisher} invokes its {@code Subscriber}'s {@code onError} method, and then quits + * without invoking any more of its {@code Subscriber}'s methods. The {@code onErrorReturn} method changes this + * behavior. If you pass a function ({@code resumeFunction}) to a {@code Publisher}'s {@code onErrorReturn} + * method, if the original {@code Publisher} encounters an error, instead of invoking its {@code Subscriber}'s * {@code onError} method, it will instead emit the return value of {@code resumeFunction}. *

* You can use this to prevent errors from propagating or to supply fallback data should errors be * encountered. *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher}s is expected to honor + *
The operator honors backpressure from downstream. The current {@code Flowable} is expected to honor * backpressure as well. If it this expectation is violated, the operator may throw - * {@code IllegalStateException} when the source {@code Publisher} completes or - * {@code MissingBackpressureException} is signaled somewhere downstream.
+ * {@link IllegalStateException} when the current {@code Flowable} completes or + * {@link MissingBackpressureException} is signaled somewhere downstream. *
Scheduler:
*
{@code onErrorReturnItem} does not operate by default on a particular {@link Scheduler}.
*
* * @param item - * the value that is emitted along with a regular onComplete in case the current - * Flowable signals an exception - * @return the original Publisher with appropriately modified behavior + * the value that is emitted along with a regular {@code onComplete} in case the current + * {@code Flowable} signals an exception + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable onErrorReturnItem(final T item) { - ObjectHelper.requireNonNull(item, "item is null"); + public final Flowable onErrorReturnItem(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); return onErrorReturn(Functions.justFunction(item)); } /** - * Nulls out references to the upstream producer and downstream Subscriber if + * Nulls out references to the upstream producer and downstream {@link Subscriber} if * the sequence is terminated or downstream cancels. *
*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code onTerminateDetach} does not operate by default on a particular {@link Scheduler}.
*
- * @return a Flowable which nulls out references to the upstream producer and downstream Subscriber if + * @return the new {@code Flowable} instance * the sequence is terminated or downstream cancels * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable onTerminateDetach() { - return RxJavaPlugins.onAssembly(new FlowableDetach(this)); + return RxJavaPlugins.onAssembly(new FlowableDetach<>(this)); } /** @@ -12116,7 +13224,7 @@ public final Flowable onTerminateDetach() { * and dispatches the upstream items to them in a round-robin fashion. *

* Note that the rails don't execute in parallel on their own and one needs to - * apply {@link ParallelFlowable#runOn(Scheduler)} to specify the Scheduler where + * apply {@link ParallelFlowable#runOn(Scheduler)} to specify the {@link Scheduler} where * each rail will execute. *

* To merge the parallel 'rails' back into a single sequence, use {@link ParallelFlowable#sequential()}. @@ -12127,15 +13235,16 @@ public final Flowable onTerminateDetach() { *

The operator requires the upstream to honor backpressure and each 'rail' honors backpressure * as well.
*
Scheduler:
- *
{@code parallel} does not operate by default on a particular {@link Scheduler}.
+ *
{@code parallel} does not operate by default on a particular {@code Scheduler}.
*
*

History: 2.0.5 - experimental; 2.1 - beta - * @return the new ParallelFlowable instance + * @return the new {@link ParallelFlowable} instance * @since 2.2 */ @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue + @NonNull public final ParallelFlowable parallel() { return ParallelFlowable.from(this); } @@ -12145,7 +13254,7 @@ public final ParallelFlowable parallel() { * and dispatches the upstream items to them in a round-robin fashion. *

* Note that the rails don't execute in parallel on their own and one needs to - * apply {@link ParallelFlowable#runOn(Scheduler)} to specify the Scheduler where + * apply {@link ParallelFlowable#runOn(Scheduler)} to specify the {@link Scheduler} where * each rail will execute. *

* To merge the parallel 'rails' back into a single sequence, use {@link ParallelFlowable#sequential()}. @@ -12156,18 +13265,19 @@ public final ParallelFlowable parallel() { *

The operator requires the upstream to honor backpressure and each 'rail' honors backpressure * as well.
*
Scheduler:
- *
{@code parallel} does not operate by default on a particular {@link Scheduler}.
+ *
{@code parallel} does not operate by default on a particular {@code Scheduler}.
*
*

History: 2.0.5 - experimental; 2.1 - beta * @param parallelism the number of 'rails' to use - * @return the new ParallelFlowable instance + * @return the new {@link ParallelFlowable} instance + * @throws IllegalArgumentException if {@code parallelism} is non-positive * @since 2.2 */ @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue + @NonNull public final ParallelFlowable parallel(int parallelism) { - ObjectHelper.verifyPositive(parallelism, "parallelism"); return ParallelFlowable.from(this, parallelism); } @@ -12177,7 +13287,7 @@ public final ParallelFlowable parallel(int parallelism) { * uses the defined per-'rail' prefetch amount. *

* Note that the rails don't execute in parallel on their own and one needs to - * apply {@link ParallelFlowable#runOn(Scheduler)} to specify the Scheduler where + * apply {@link ParallelFlowable#runOn(Scheduler)} to specify the {@link Scheduler} where * each rail will execute. *

* To merge the parallel 'rails' back into a single sequence, use {@link ParallelFlowable#sequential()}. @@ -12188,152 +13298,157 @@ public final ParallelFlowable parallel(int parallelism) { *

The operator requires the upstream to honor backpressure and each 'rail' honors backpressure * as well.
*
Scheduler:
- *
{@code parallel} does not operate by default on a particular {@link Scheduler}.
+ *
{@code parallel} does not operate by default on a particular {@code Scheduler}.
*
*

History: 2.0.5 - experimental; 2.1 - beta * @param parallelism the number of 'rails' to use * @param prefetch the number of items each 'rail' should prefetch - * @return the new ParallelFlowable instance + * @return the new {@link ParallelFlowable} instance + * @throws IllegalArgumentException if {@code parallelism} or {@code prefetch} is non-positive * @since 2.2 */ @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue + @NonNull public final ParallelFlowable parallel(int parallelism, int prefetch) { - ObjectHelper.verifyPositive(parallelism, "parallelism"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); return ParallelFlowable.from(this, parallelism, prefetch); } /** - * Returns a {@link ConnectableFlowable}, which is a variety of Publisher that waits until its + * Returns a {@link ConnectableFlowable}, which is a variety of {@link Publisher} that waits until its * {@link ConnectableFlowable#connect connect} method is called before it begins emitting items to those * {@link Subscriber}s that have subscribed to it. *

- * + * *

*
Backpressure:
*
The returned {@code ConnectableFlowable} honors backpressure for each of its {@code Subscriber}s - * and expects the source {@code Publisher} to honor backpressure as well. If this expectation is violated, - * the operator will signal a {@code MissingBackpressureException} to its {@code Subscriber}s and disconnect.
+ * and expects the current {@code Flowable} to honor backpressure as well. If this expectation is violated, + * the operator will signal a {@link MissingBackpressureException} to its {@code Subscriber}s and disconnect. *
Scheduler:
*
{@code publish} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link ConnectableFlowable} that upon connection causes the source Publisher to emit items - * to its {@link Subscriber}s + * @return the new {@code ConnectableFlowable} instance * @see ReactiveX operators documentation: Publish */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final ConnectableFlowable publish() { return publish(bufferSize()); } /** - * Returns a Flowable that emits the results of invoking a specified selector on items emitted by a + * Returns a {@code Flowable} that emits the results of invoking a specified selector on items emitted by a * {@link ConnectableFlowable} that shares a single subscription to the underlying sequence. *

- * + * *

*
Backpressure:
- *
The operator expects the source {@code Publisher} to honor backpressure and if this expectation is - * violated, the operator will signal a {@code MissingBackpressureException} through the {@code Publisher} - * provided to the function. Since the {@code Publisher} returned by the {@code selector} may be - * independent of the provided {@code Publisher} to the function, the output's backpressure behavior + *
The operator expects the current {@code Flowable} to honor backpressure and if this expectation is + * violated, the operator will signal a {@link MissingBackpressureException} through the {@code Flowable} + * provided to the function. Since the {@link Publisher} returned by the {@code selector} may be + * independent of the provided {@code Flowable} to the function, the output's backpressure behavior * is determined by this returned {@code Publisher}.
*
Scheduler:
*
{@code publish} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @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 + * causing multiple subscriptions to the source sequence. {@link Subscriber}s to the given source will * receive all notifications of the source from the time of the subscription forward. - * @return a Flowable that emits the results of invoking the selector on the items emitted by a {@link ConnectableFlowable} that shares a single subscription to the underlying sequence + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} is {@code null} * @see ReactiveX operators documentation: Publish */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable publish(Function, ? extends Publisher> selector) { + @NonNull + public final <@NonNull R> Flowable publish(@NonNull Function, @NonNull ? extends Publisher> selector) { return publish(selector, bufferSize()); } /** - * Returns a Flowable that emits the results of invoking a specified selector on items emitted by a + * Returns a {@code Flowable} that emits the results of invoking a specified selector on items emitted by a * {@link ConnectableFlowable} that shares a single subscription to the underlying sequence. *

- * + * *

*
Backpressure:
- *
The operator expects the source {@code Publisher} to honor backpressure and if this expectation is - * violated, the operator will signal a {@code MissingBackpressureException} through the {@code Publisher} - * provided to the function. Since the {@code Publisher} returned by the {@code selector} may be - * independent of the provided {@code Publisher} to the function, the output's backpressure behavior + *
The operator expects the current {@code Flowable} to honor backpressure and if this expectation is + * violated, the operator will signal a {@link MissingBackpressureException} through the {@code Flowable} + * provided to the function. Since the {@link Publisher} returned by the {@code selector} may be + * independent of the provided {@code Flowable} to the function, the output's backpressure behavior * is determined by this returned {@code Publisher}.
*
Scheduler:
*
{@code publish} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @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 + * causing multiple subscriptions to the source sequence. {@link Subscriber}s to the given source will * receive all notifications of the source from the time of the subscription forward. * @param prefetch - * the number of elements to prefetch from the current Flowable - * @return a Flowable that emits the results of invoking the selector on the items emitted by a {@link ConnectableFlowable} that shares a single subscription to the underlying sequence + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ReactiveX operators documentation: Publish */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable publish(Function, ? extends Publisher> selector, int prefetch) { - ObjectHelper.requireNonNull(selector, "selector is null"); + public final <@NonNull R> Flowable publish(@NonNull Function, @NonNull ? extends Publisher> selector, int prefetch) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowablePublishMulticast(this, selector, prefetch, false)); + return RxJavaPlugins.onAssembly(new FlowablePublishMulticast<>(this, selector, prefetch, false)); } /** - * Returns a {@link ConnectableFlowable}, which is a variety of Publisher that waits until its + * Returns a {@link ConnectableFlowable}, which is a variety of {@link Publisher} that waits until its * {@link ConnectableFlowable#connect connect} method is called before it begins emitting items to those * {@link Subscriber}s that have subscribed to it. *

- * + * *

*
Backpressure:
*
The returned {@code ConnectableFlowable} honors backpressure for each of its {@code Subscriber}s - * and expects the source {@code Publisher} to honor backpressure as well. If this expectation is violated, - * the operator will signal a {@code MissingBackpressureException} to its {@code Subscriber}s and disconnect.
+ * and expects the current {@code Flowable} to honor backpressure as well. If this expectation is violated, + * the operator will signal a {@link MissingBackpressureException} to its {@code Subscriber}s and disconnect. *
Scheduler:
*
{@code publish} does not operate by default on a particular {@link Scheduler}.
*
* * @param bufferSize - * the number of elements to prefetch from the current Flowable - * @return a {@link ConnectableFlowable} that upon connection causes the source Publisher to emit items - * to its {@link Subscriber}s + * the number of elements to prefetch from the current {@code Flowable} + * @return the new {@code ConnectableFlowable} instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Publish */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final ConnectableFlowable publish(int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowablePublish(this, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowablePublish<>(this, bufferSize)); } /** * Requests {@code n} initially from the upstream and then 75% of {@code n} subsequently * after 75% of {@code n} values have been emitted to the downstream. * - *

This operator allows preventing the downstream to trigger unbounded mode via {@code request(Long.MAX_VALUE)} + *

This operator allows preventing the downstream to trigger unbounded mode via {@code request(}{@link Long#MAX_VALUE}{@code )} * or compensate for the per-item overhead of small and frequent requests. * *

@@ -12344,23 +13459,25 @@ public final ConnectableFlowable publish(int bufferSize) { *
* * @param n the initial request amount, further request will happen after 75% of this value - * @return the Publisher that rebatches request amounts from downstream + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code n} is non-positive * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable rebatchRequests(int n) { return observeOn(ImmediateThinScheduler.INSTANCE, true, n); } /** - * Returns a Maybe that applies a specified accumulator function to the first item emitted by a source - * Publisher, then feeds the result of that function along with the second item emitted by the source - * Publisher into the same function, and so on until all items have been emitted by the finite source Publisher, + * Returns a {@link Maybe} that applies a specified accumulator function to the first item emitted by the current + * {@code Flowable}, then feeds the result of that function along with the second item emitted by the current + * {@code Flowable} into the same function, and so on until all items have been emitted by the current and finite {@code Flowable}, * and emits the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -12368,7 +13485,7 @@ public final Flowable rebatchRequests(int n) { *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
*
The operator honors backpressure of its downstream consumer and consumes the @@ -12378,10 +13495,10 @@ public final Flowable rebatchRequests(int n) { *
* * @param reducer - * an accumulator function to be invoked on each item emitted by the source Publisher, whose + * an accumulator function to be invoked on each item emitted by the current {@code Flowable}, whose * result will be used in the next accumulator call - * @return a Maybe that emits a single item that is the result of accumulating the items emitted by - * the source Flowable + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code reducer} is {@code null} * @see ReactiveX operators documentation: Reduce * @see Wikipedia: Fold (higher-order function) */ @@ -12389,28 +13506,28 @@ public final Flowable rebatchRequests(int n) { @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe reduce(BiFunction reducer) { - ObjectHelper.requireNonNull(reducer, "reducer is null"); - return RxJavaPlugins.onAssembly(new FlowableReduceMaybe(this, reducer)); + public final Maybe reduce(@NonNull BiFunction reducer) { + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new FlowableReduceMaybe<>(this, reducer)); } /** - * Returns a Single that applies a specified accumulator function to the first item emitted by a source - * Publisher and a specified seed value, then feeds the result of that function along with the second item - * emitted by a Publisher into the same function, and so on until all items have been emitted by the - * finite source Publisher, emitting the final result from the final call to your function as its sole item. + * Returns a {@link Single} that applies a specified accumulator function to the first item emitted by the current + * {@code Flowable} and a specified seed value, then feeds the result of that function along with the second item + * emitted by the current {@code Flowable} into the same function, and so on until all items have been emitted by the + * current and finite {@code Flowable}, emitting the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method * that does a similar operation on lists. *

- * Note that the {@code seed} is shared among all subscribers to the resulting Publisher + * Note that the {@code seed} is shared among all subscribers to the resulting {@code Flowable} * and may cause problems if it is mutable. To make sure each subscriber gets its own value, defer * the application of this operator via {@link #defer(Supplier)}: *


-     * Publisher<T> source = ...
+     * Flowable<T> source = ...
      * Single.defer(() -> source.reduce(new ArrayList<>(), (list, item) -> list.add(item)));
      *
      * // alternatively, by using compose to stay fluent
@@ -12426,7 +13543,7 @@ public final Maybe reduce(BiFunction reducer) {
      * 

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
*
The operator honors backpressure of its downstream consumer and consumes the @@ -12439,10 +13556,10 @@ public final Maybe reduce(BiFunction reducer) { * @param seed * the initial (seed) accumulator value * @param reducer - * an accumulator function to be invoked on each item emitted by the source Publisher, the + * an accumulator function to be invoked on each item emitted by the current {@code Flowable}, the * result of which will be used in the next accumulator call - * @return a Single that emits a single item that is the result of accumulating the output from the - * items emitted by the source Publisher + * @return the new {@code Single} instance + * @throws NullPointerException if {@code seed} or {@code reducer} is {@code null} * @see ReactiveX operators documentation: Reduce * @see Wikipedia: Fold (higher-order function) * @see #reduceWith(Supplier, BiFunction) @@ -12451,28 +13568,28 @@ public final Maybe reduce(BiFunction reducer) { @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single reduce(R seed, BiFunction reducer) { - ObjectHelper.requireNonNull(seed, "seed is null"); - ObjectHelper.requireNonNull(reducer, "reducer is null"); - return RxJavaPlugins.onAssembly(new FlowableReduceSeedSingle(this, seed, reducer)); + public final <@NonNull R> Single reduce(R seed, @NonNull BiFunction reducer) { + Objects.requireNonNull(seed, "seed is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new FlowableReduceSeedSingle<>(this, seed, reducer)); } /** - * Returns a Single that applies a specified accumulator function to the first item emitted by a source - * Publisher and a seed value derived from calling a specified seedSupplier, then feeds the result - * of that function along with the second item emitted by a Publisher into the same function, and so on until - * all items have been emitted by the finite source Publisher, emitting the final result from the final call to your + * Returns a {@link Single} that applies a specified accumulator function to the first item emitted by the current + * {@code Flowable} and a seed value derived from calling a specified {@code seedSupplier}, then feeds the result + * of that function along with the second item emitted by the current {@code Flowable} into the same function, and so on until + * all items have been emitted by the current and finite {@code Flowable}, emitting the final result from the final call to your * function as its sole item. *

- * + * *

- * This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," - * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method + * This technique, which is called "reduce" here, is sometimes called "aggregate", "fold", "accumulate", + * "compress", or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method * that does a similar operation on lists. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
*
The operator honors backpressure of its downstream consumer and consumes the @@ -12483,12 +13600,12 @@ public final Single reduce(R seed, BiFunction reducer) { * * @param the accumulator and output value type * @param seedSupplier - * the Supplier that provides the initial (seed) accumulator value for each individual Subscriber + * the {@link Supplier} that provides the initial (seed) accumulator value for each individual {@link Subscriber} * @param reducer - * an accumulator function to be invoked on each item emitted by the source Publisher, the + * an accumulator function to be invoked on each item emitted by the current {@code Flowable}, the * result of which will be used in the next accumulator call - * @return a Single that emits a single item that is the result of accumulating the output from the - * items emitted by the source Publisher + * @return the new {@code Single} instance + * @throws NullPointerException if {@code seedSupplier} or {@code reducer} is {@code null} * @see ReactiveX operators documentation: Reduce * @see Wikipedia: Fold (higher-order function) */ @@ -12496,59 +13613,60 @@ public final Single reduce(R seed, BiFunction reducer) { @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single reduceWith(Supplier seedSupplier, BiFunction reducer) { - ObjectHelper.requireNonNull(seedSupplier, "seedSupplier is null"); - ObjectHelper.requireNonNull(reducer, "reducer is null"); - return RxJavaPlugins.onAssembly(new FlowableReduceWithSingle(this, seedSupplier, reducer)); + public final <@NonNull R> Single reduceWith(@NonNull Supplier seedSupplier, @NonNull BiFunction reducer) { + Objects.requireNonNull(seedSupplier, "seedSupplier is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new FlowableReduceWithSingle<>(this, seedSupplier, reducer)); } /** - * Returns a Flowable that repeats the sequence of items emitted by the source Publisher indefinitely. + * Returns a {@code Flowable} that repeats the sequence of items emitted by the current {@code Flowable} indefinitely. *

- * + * *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits the items emitted by the source Publisher repeatedly and in sequence + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable repeat() { return repeat(Long.MAX_VALUE); } /** - * Returns a Flowable that repeats the sequence of items emitted by the source Publisher at most + * Returns a {@code Flowable} that repeats the sequence of items emitted by the current {@code Flowable} at most * {@code count} times. *

- * + * *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
* * @param times - * the number of times the source Publisher items are repeated, a count of 0 will yield an empty + * the number of times the current {@code Flowable} items are repeated, a count of 0 will yield an empty * sequence - * @return a Flowable that repeats the sequence of items emitted by the source Publisher at most - * {@code count} times + * @return the new {@code Flowable} instance * @throws IllegalArgumentException - * if {@code count} is less than zero + * if {@code times} is less than zero * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable repeat(long times) { if (times < 0) { throw new IllegalArgumentException("times >= 0 required but it was " + times); @@ -12556,157 +13674,158 @@ public final Flowable repeat(long times) { if (times == 0) { return empty(); } - return RxJavaPlugins.onAssembly(new FlowableRepeat(this, times)); + return RxJavaPlugins.onAssembly(new FlowableRepeat<>(this, times)); } /** - * Returns a Flowable that repeats the sequence of items emitted by the source Publisher until - * the provided stop function returns true. + * Returns a {@code Flowable} that repeats the sequence of items emitted by the current {@code Flowable} until + * the provided stop function returns {@code true}. *

- * + * *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code repeatUntil} does not operate by default on a particular {@link Scheduler}.
*
* * @param stop - * a boolean supplier that is called when the current Flowable completes and unless it returns - * false, the current Flowable is resubscribed - * @return the new Flowable instance + * a boolean supplier that is called when the current {@code Flowable} completes and unless it returns + * {@code false}, the current {@code Flowable} is resubscribed + * @return the new {@code Flowable} instance * @throws NullPointerException - * if {@code stop} is null + * if {@code stop} is {@code null} * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable repeatUntil(BooleanSupplier stop) { - ObjectHelper.requireNonNull(stop, "stop is null"); - return RxJavaPlugins.onAssembly(new FlowableRepeatUntil(this, stop)); + public final Flowable repeatUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); + return RxJavaPlugins.onAssembly(new FlowableRepeatUntil<>(this, stop)); } /** - * Returns a Flowable that emits the same values as the source Publisher with the exception of an + * Returns a {@code Flowable} that emits the same values as the current {@code Flowable} with the exception of an * {@code onComplete}. An {@code onComplete} notification from the source will result in the emission of - * a {@code void} item to the Publisher provided as an argument to the {@code notificationHandler} - * function. If that Publisher calls {@code onComplete} or {@code onError} then {@code repeatWhen} will - * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, this Publisher will - * resubscribe to the source Publisher. + * a {@code void} item to the {@code Flowable} provided as an argument to the {@code notificationHandler} + * function. If that {@link Publisher} calls {@code onComplete} or {@code onError} then {@code repeatWhen} will + * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, this {@code Publisher} will + * resubscribe to the current {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code repeatWhen} does not operate by default on a particular {@link Scheduler}.
*
* * @param handler - * receives a Publisher of notifications with which a user can complete or error, aborting the repeat. - * @return the source Publisher modified with repeat logic + * receives a {@code Publisher} of notifications with which a user can complete or error, aborting the repeat. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code handler} is {@code null} * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable repeatWhen(final Function, ? extends Publisher> handler) { - ObjectHelper.requireNonNull(handler, "handler is null"); - return RxJavaPlugins.onAssembly(new FlowableRepeatWhen(this, handler)); + public final Flowable repeatWhen(@NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { + Objects.requireNonNull(handler, "handler is null"); + return RxJavaPlugins.onAssembly(new FlowableRepeatWhen<>(this, handler)); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the underlying Publisher - * that will replay all of its items and notifications to any future {@link Subscriber}. A Connectable - * Publisher resembles an ordinary Publisher, except that it does not begin emitting items when it is + * Returns a {@link ConnectableFlowable} that shares a single subscription to the underlying {@link Publisher} + * that will replay all of its items and notifications to any future {@link Subscriber}. A connectable + * {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence.
*
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link ConnectableFlowable} that upon connection causes the source Publisher to emit its - * items to its {@link Subscriber}s + * @return the new {@code ConnectableFlowable} instance * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final ConnectableFlowable replay() { return FlowableReplay.createFrom(this); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on the items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher. + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on the items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence.
*
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher - * @return a Flowable that emits items that are the results of invoking the selector on a - * {@link ConnectableFlowable} that shares a single subscription to the source Publisher + * causing multiple subscriptions to the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable replay(Function, ? extends Publisher> selector) { - ObjectHelper.requireNonNull(selector, "selector is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector) { + Objects.requireNonNull(selector, "selector is null"); return FlowableReplay.multicastSelector(FlowableInternalHelper.replaySupplier(this), selector); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying {@code bufferSize} notifications. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param bufferSize - * the buffer size that limits the number of items the connectable Publisher can replay - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher - * replaying no more than {@code bufferSize} items + * the buffer size that limits the number of items the operator can replay + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(Function, int, boolean) */ @@ -12714,134 +13833,132 @@ public final Flowable replay(Function, ? extends Publ @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable replay(Function, ? extends Publisher> selector, final int bufferSize) { - ObjectHelper.requireNonNull(selector, "selector is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, int bufferSize) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return FlowableReplay.multicastSelector(FlowableInternalHelper.replaySupplier(this, bufferSize, false), selector); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying {@code bufferSize} notifications. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param bufferSize - * the buffer size that limits the number of items the connectable Publisher can replay + * the buffer size that limits the number of items the operator can replay * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher - * replaying no more than {@code bufferSize} items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable replay(Function, ? extends Publisher> selector, final int bufferSize, boolean eagerTruncate) { - ObjectHelper.requireNonNull(selector, "selector is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, int bufferSize, boolean eagerTruncate) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return FlowableReplay.multicastSelector(FlowableInternalHelper.replaySupplier(this, bufferSize, eagerTruncate), selector); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying no more than {@code bufferSize} items that were emitted within a specified time window. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param bufferSize - * the buffer size that limits the number of items the connectable Publisher can replay + * the buffer size that limits the number of items the operator can replay * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, and - * replays no more than {@code bufferSize} items that were emitted within the window defined by - * {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} or {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable replay(Function, ? extends Publisher> selector, int bufferSize, long time, TimeUnit unit) { + @NonNull + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, int bufferSize, long time, @NonNull TimeUnit unit) { return replay(selector, bufferSize, time, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying no more than {@code bufferSize} items that were emitted within a specified time window. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param bufferSize - * the buffer size that limits the number of items the connectable Publisher can replay + * the buffer size that limits the number of items the operator can replay * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, and - * replays no more than {@code bufferSize} items that were emitted within the window defined by - * {@code time} + * the {@code Scheduler} that is the time source for the window + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(Function, int, long, TimeUnit, Scheduler, boolean) */ @@ -12849,53 +13966,51 @@ public final Flowable replay(Function, ? extends Publ @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable replay(Function, ? extends Publisher> selector, final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(selector, "selector is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(selector, "selector is null"); + Objects.requireNonNull(unit, "unit is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return FlowableReplay.multicastSelector( FlowableInternalHelper.replaySupplier(this, bufferSize, time, unit, scheduler, false), selector); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying no more than {@code bufferSize} items that were emitted within a specified time window. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param bufferSize - * the buffer size that limits the number of items the connectable Publisher can replay + * the buffer size that limits the number of items the operator can replay * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window + * the {@code Scheduler} that is the time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, and - * replays no more than {@code bufferSize} items that were emitted within the window defined by - * {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @throws IllegalArgumentException * if {@code bufferSize} is less than zero * @see ReactiveX operators documentation: Replay @@ -12904,80 +14019,79 @@ public final Flowable replay(Function, ? extends Publ @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable replay(Function, ? extends Publisher> selector, final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.requireNonNull(selector, "selector is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(selector, "selector is null"); + Objects.requireNonNull(unit, "unit is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return FlowableReplay.multicastSelector( FlowableInternalHelper.replaySupplier(this, bufferSize, time, unit, scheduler, eagerTruncate), selector); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, - * replaying all items that were emitted within the window defined by {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector} or {@code unit} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable replay(Function, ? extends Publisher> selector, long time, TimeUnit unit) { + @NonNull + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, long time, @NonNull TimeUnit unit) { return replay(selector, time, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} * @param scheduler * the scheduler that is the time source for the window - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, - * replaying all items that were emitted within the window defined by {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay * @see #replay(Function, long, TimeUnit, Scheduler, boolean) */ @@ -12985,33 +14099,33 @@ public final Flowable replay(Function, ? extends Publ @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable replay(Function, ? extends Publisher> selector, final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(selector, "selector is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(selector, "selector is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return FlowableReplay.multicastSelector(FlowableInternalHelper.replaySupplier(this, time, unit, scheduler, false), selector); } /** - * Returns a Flowable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, + * Returns a {@code Flowable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the Publisher + * causing multiple subscriptions to the current {@code Flowable} * @param time * the duration of the window in which the replayed items must have been emitted * @param unit @@ -13019,28 +14133,27 @@ public final Flowable replay(Function, ? extends Publ * @param scheduler * the scheduler that is the time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given age, the + * if {@code true}, whenever the internal buffer is truncated to the given age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a Flowable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableFlowable} that shares a single subscription to the source Publisher, - * replaying all items that were emitted within the window defined by {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable replay(Function, ? extends Publisher> selector, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.requireNonNull(selector, "selector is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final <@NonNull R> Flowable replay(@NonNull Function, @NonNull ? extends Publisher> selector, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(selector, "selector is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return FlowableReplay.multicastSelector(FlowableInternalHelper.replaySupplier(this, time, unit, scheduler, eagerTruncate), selector); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher that - * replays at most {@code bufferSize} items emitted by that Publisher. A Connectable Publisher resembles - * an ordinary Publisher, except that it does not begin emitting items when it is subscribed to, but only + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays at most {@code bufferSize} items to late {@link Subscriber}s. A Connectable {@code Flowable} resembles + * an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than @@ -13048,37 +14161,38 @@ public final Flowable replay(Function, ? extends Publ * To ensure no beyond-bufferSize items are referenced, * use the {@link #replay(int, boolean)} overload with {@code eagerTruncate = true}. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* * @param bufferSize * the buffer size that limits the number of items that can be replayed - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays at most {@code bufferSize} items emitted by that Publisher + * @return the new {@code ConnectableFlowable} instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(int, boolean) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final ConnectableFlowable replay(final int bufferSize) { + @NonNull + public final ConnectableFlowable replay(int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return FlowableReplay.create(this, bufferSize, false); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher that - * replays at most {@code bufferSize} items emitted by that Publisher. A Connectable Publisher resembles - * an ordinary Publisher, except that it does not begin emitting items when it is subscribed to, but only + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays at most {@code bufferSize} items to late {@link Subscriber}s. A connectable {@code Flowable} resembles + * an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -13086,8 +14200,8 @@ public final ConnectableFlowable replay(final int bufferSize) { *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
@@ -13095,28 +14209,29 @@ public final ConnectableFlowable replay(final int bufferSize) { * @param bufferSize * the buffer size that limits the number of items that can be replayed * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays at most {@code bufferSize} items emitted by that Publisher + * @return the new {@code ConnectableFlowable} instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @since 3.0.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final ConnectableFlowable replay(final int bufferSize, boolean eagerTruncate) { + @NonNull + public final ConnectableFlowable replay(int bufferSize, boolean eagerTruncate) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return FlowableReplay.create(this, bufferSize, eagerTruncate); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays at most {@code bufferSize} items that were emitted during a specified time window. A Connectable - * Publisher resembles an ordinary Publisher, except that it does not begin emitting items when it is + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays at most {@code bufferSize} items that were emitted during a specified time window. A connectable + * {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -13125,8 +14240,8 @@ public final ConnectableFlowable replay(final int bufferSize, boolean eagerTr *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@link Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
*
@@ -13137,26 +14252,27 @@ public final ConnectableFlowable replay(final int bufferSize, boolean eagerTr * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays at most {@code bufferSize} items that were emitted during the window defined by - * {@code time} + * @return the new {@code ConnectableFlowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(int, long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final ConnectableFlowable replay(int bufferSize, long time, TimeUnit unit) { + @NonNull + public final ConnectableFlowable replay(int bufferSize, long time, @NonNull TimeUnit unit) { return replay(bufferSize, time, unit, Schedulers.computation()); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * that replays a maximum of {@code bufferSize} items that are emitted within a specified time window. A - * Connectable Publisher resembles an ordinary Publisher, except that it does not begin emitting items + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays a maximum of {@code bufferSize} items that are emitted within a specified time window to late {@link Subscriber}s. A + * connectable {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items * when it is subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -13165,8 +14281,8 @@ public final ConnectableFlowable replay(int bufferSize, long time, TimeUnit u *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
@@ -13179,41 +14295,39 @@ public final ConnectableFlowable replay(int bufferSize, long time, TimeUnit u * the time unit of {@code time} * @param scheduler * the scheduler that is used as a time source for the window - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays at most {@code bufferSize} items that were emitted during the window defined by - * {@code time} - * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * @return the new {@code ConnectableFlowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(int, long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableFlowable replay(final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final ConnectableFlowable replay(int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return FlowableReplay.create(this, time, unit, scheduler, bufferSize, false); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * that replays a maximum of {@code bufferSize} items that are emitted within a specified time window. A - * Connectable Publisher resembles an ordinary Publisher, except that it does not begin emitting items + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays a maximum of {@code bufferSize} items that are emitted within a specified time window to late {@link Subscriber}s. A + * connectable {@code Flowable} resembles an ordinary {@code Flowable}, except that it does not begin emitting items * when it is subscribed to, but only when its {@code connect} method is called. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. To ensure no out-of-date or beyond-bufferSize items * are referenced, set {@code eagerTruncate = true}. *

- * + * *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
@@ -13227,42 +14341,40 @@ public final ConnectableFlowable replay(final int bufferSize, final long time * @param scheduler * the scheduler that is used as a time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays at most {@code bufferSize} items that were emitted during the window defined by - * {@code time} - * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * @return the new {@code ConnectableFlowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @since 3.0.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableFlowable replay(final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final ConnectableFlowable replay(int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return FlowableReplay.create(this, time, unit, scheduler, bufferSize, eagerTruncate); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays all items emitted by that Publisher within a specified time window. A Connectable Publisher - * resembles an ordinary Publisher, except that it does not begin emitting items when it is subscribed to, + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays all items emitted by it within a specified time window to late {@link Subscriber}s. A connectable {@code Flowable} + * resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, use the {@link #replay(long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
*
@@ -13271,32 +14383,33 @@ public final ConnectableFlowable replay(final int bufferSize, final long time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays the items that were emitted during the window defined by {@code time} + * @return the new {@code ConnectableFlowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final ConnectableFlowable replay(long time, TimeUnit unit) { + @NonNull + public final ConnectableFlowable replay(long time, @NonNull TimeUnit unit) { return replay(time, unit, Schedulers.computation()); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays all items emitted by that Publisher within a specified time window. A Connectable Publisher - * resembles an ordinary Publisher, except that it does not begin emitting items when it is subscribed to, + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays all items emitted by it within a specified time window to late {@link Subscriber}s. A connectable {@code Flowable} + * resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, use the {@link #replay(long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
@@ -13306,36 +14419,37 @@ public final ConnectableFlowable replay(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays the items that were emitted during the window defined by {@code time} + * the {@code Scheduler} that is the time source for the window + * @return the new {@code ConnectableFlowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay * @see #replay(long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableFlowable replay(final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final ConnectableFlowable replay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return FlowableReplay.create(this, time, unit, scheduler, false); } /** - * Returns a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays all items emitted by that Publisher within a specified time window. A Connectable Publisher - * resembles an ordinary Publisher, except that it does not begin emitting items when it is subscribed to, + * Returns a {@link ConnectableFlowable} that shares a single subscription to the current {@code Flowable} and + * replays all items emitted by it within a specified time window to late {@link Subscriber}s. A connectable {@code Flowable} + * resembles an ordinary {@code Flowable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, set {@code eagerTruncate = true}. *

*
Backpressure:
*
This operator supports backpressure. Note that the upstream requests are determined by the child - * Subscriber which requests the largest amount: i.e., two child Subscribers with requests of 10 and 100 will - * request 100 elements from the underlying Publisher sequence.
+ * {@code Subscriber} which requests the largest amount: i.e., two child {@code Subscriber}s with requests of 10 and 100 will + * request 100 elements from the current {@code Flowable} sequence. *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
@@ -13345,63 +14459,65 @@ public final ConnectableFlowable replay(final long time, final TimeUnit unit, * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window + * the {@code Scheduler} that is the time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a {@link ConnectableFlowable} that shares a single subscription to the source Publisher and - * replays the items that were emitted during the window defined by {@code time} + * @return the new {@code ConnectableFlowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableFlowable replay(final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final ConnectableFlowable replay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return FlowableReplay.create(this, time, unit, scheduler, eagerTruncate); } /** - * Returns a Flowable that mirrors the source Publisher, resubscribing to it if it calls {@code onError} + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, resubscribing to it if it calls {@code onError} * (infinite retry count). *

- * + * *

- * If the source Publisher calls {@link Subscriber#onError}, this method will resubscribe to the source - * Publisher rather than propagating the {@code onError} call. + * If the current {@code Flowable} calls {@link Subscriber#onError}, this method will resubscribe to the current + * {@code Flowable} rather than propagating the {@code onError} call. *

- * Any and all items emitted by the source Publisher will be emitted by the resulting Publisher, even - * those emitted during failed subscriptions. For example, if a Publisher fails at first but emits + * Any and all items emitted by the current {@code Flowable} will be emitted by the resulting {@code Flowable}, even + * those emitted during failed subscriptions. For example, if the current {@code Flowable} fails at first but emits * {@code [1, 2]} then succeeds the second time and emits {@code [1, 2, 3, 4, 5]} then the complete sequence * of emissions and notifications would be {@code [1, 2, 1, 2, 3, 4, 5, onComplete]}. *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source Publisher modified with retry logic + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable retry() { return retry(Long.MAX_VALUE, Functions.alwaysTrue()); } /** - * Returns a Flowable that mirrors the source Publisher, resubscribing to it if it calls {@code onError} - * and the predicate returns true for that specific exception and retry count. + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, resubscribing to it if it calls {@code onError} + * and the predicate returns {@code true} for that specific exception and retry count. *

- * + * *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
@@ -13409,7 +14525,8 @@ public final Flowable retry() { * @param predicate * the predicate that determines if a resubscription may happen in case of a specific exception * and retry count - * @return the source Publisher modified with retry logic + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see #retry() * @see ReactiveX operators documentation: Retry */ @@ -13417,123 +14534,130 @@ public final Flowable retry() { @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable retry(BiPredicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); + public final Flowable retry(@NonNull BiPredicate<@NonNull ? super Integer, @NonNull ? super Throwable> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableRetryBiPredicate(this, predicate)); + return RxJavaPlugins.onAssembly(new FlowableRetryBiPredicate<>(this, predicate)); } /** - * Returns a Flowable that mirrors the source Publisher, resubscribing to it if it calls {@code onError} + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, resubscribing to it if it calls {@code onError} * up to a specified number of retries. *

- * + * *

- * If the source Publisher calls {@link Subscriber#onError}, this method will resubscribe to the source - * Publisher for a maximum of {@code count} resubscriptions rather than propagating the + * If the current {@code Flowable} calls {@link Subscriber#onError}, this method will resubscribe to the current + * {@code Flowable} for a maximum of {@code count} resubscriptions rather than propagating the * {@code onError} call. *

- * Any and all items emitted by the source Publisher will be emitted by the resulting Publisher, even - * those emitted during failed subscriptions. For example, if a Publisher fails at first but emits + * Any and all items emitted by the current {@code Flowable} will be emitted by the resulting {@code Flowable}, even + * those emitted during failed subscriptions. For example, if the current {@code Flowable} fails at first but emits * {@code [1, 2]} then succeeds the second time and emits {@code [1, 2, 3, 4, 5]} then the complete sequence * of emissions and notifications would be {@code [1, 2, 1, 2, 3, 4, 5, onComplete]}. *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @param count - * the number of times to resubscribe if the current Flowable fails - * @return the source Publisher modified with retry logic + * @param times + * the number of times to resubscribe if the current {@code Flowable} fails + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code times} is negative * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable retry(long count) { - return retry(count, Functions.alwaysTrue()); + @NonNull + public final Flowable retry(long times) { + return retry(times, Functions.alwaysTrue()); } /** - * Retries at most times or until the predicate returns false, whichever happens first. + * Retries at most times or until the predicate returns {@code false}, whichever happens first. * *
*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param times the number of times to resubscribe if the current Flowable fails - * @param predicate the predicate called with the failure Throwable and should return true to trigger a retry. - * @return the new Flowable instance + * @param times the number of times to resubscribe if the current {@code Flowable} fails + * @param predicate the predicate called with the failure {@link Throwable} and should return {@code true} to trigger a retry. + * @throws NullPointerException if {@code predicate} is {@code null} + * @throws IllegalArgumentException if {@code times} is negative + * @return the new {@code Flowable} instance */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable retry(long times, Predicate predicate) { + public final Flowable retry(long times, @NonNull Predicate<@NonNull ? super Throwable> predicate) { if (times < 0) { throw new IllegalArgumentException("times >= 0 required but it was " + times); } - ObjectHelper.requireNonNull(predicate, "predicate is null"); + Objects.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableRetryPredicate(this, times, predicate)); + return RxJavaPlugins.onAssembly(new FlowableRetryPredicate<>(this, times, predicate)); } /** - * Retries the current Flowable if the predicate returns true. + * Retries the current {@code Flowable} if the predicate returns {@code true}. *
*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @param predicate the predicate that receives the failure Throwable and should return true to trigger a retry. - * @return the new Flowable instance + * @param predicate the predicate that receives the failure {@link Throwable} and should return {@code true} to trigger a retry. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable retry(Predicate predicate) { + @NonNull + public final Flowable retry(@NonNull Predicate<@NonNull ? super Throwable> predicate) { return retry(Long.MAX_VALUE, predicate); } /** - * Retries until the given stop function returns true. + * Retries until the given stop function returns {@code true}. *
*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator may throw an {@link IllegalStateException}.
*
Scheduler:
*
{@code retryUntil} does not operate by default on a particular {@link Scheduler}.
*
- * @param stop the function that should return true to stop retrying - * @return the new Flowable instance + * @param stop the function that should return {@code true} to stop retrying + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code stop} is {@code null} */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable retryUntil(final BooleanSupplier stop) { - ObjectHelper.requireNonNull(stop, "stop is null"); + public final Flowable retryUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); return retry(Long.MAX_VALUE, Functions.predicateReverseFor(stop)); } /** - * Returns a Flowable that emits the same values as the source Publisher with the exception of an + * Returns a {@code Flowable} that emits the same values as the current {@code Flowable} with the exception of an * {@code onError}. An {@code onError} notification from the source will result in the emission of a - * {@link Throwable} item to the Publisher provided as an argument to the {@code notificationHandler} - * function. If that Publisher calls {@code onComplete} or {@code onError} then {@code retry} will call - * {@code onComplete} or {@code onError} on the child subscription. Otherwise, this Publisher will - * resubscribe to the source Publisher. + * {@link Throwable} item to the {@code Flowable} provided as an argument to the {@code notificationHandler} + * function. If that {@link Publisher} calls {@code onComplete} or {@code onError} then {@code retry} will call + * {@code onComplete} or {@code onError} on the child subscription. Otherwise, this {@code Publisher} will + * resubscribe to the current {@code Flowable}. *

- * + * *

* Example: * @@ -13566,7 +14690,7 @@ public final Flowable retryUntil(final BooleanSupplier stop) { * Note that the inner {@code Publisher} returned by the handler function should signal * either {@code onNext}, {@code onError} or {@code onComplete} in response to the received * {@code Throwable} to indicate the operator should retry or terminate. If the upstream to - * the operator is asynchronous, signaling onNext followed by onComplete immediately may + * the operator is asynchronous, signaling {@code onNext} followed by {@code onComplete} immediately may * result in the sequence to be completed immediately. Similarly, if this inner * {@code Publisher} signals {@code onError} or {@code onComplete} while the upstream is * active, the sequence is terminated with the same signal immediately. @@ -13591,15 +14715,16 @@ public final Flowable retryUntil(final BooleanSupplier stop) { *

Backpressure:
*
The operator honors downstream backpressure and expects both the source * and inner {@code Publisher}s to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ * If this expectation is violated, the operator may throw an {@link IllegalStateException}. *
Scheduler:
*
{@code retryWhen} does not operate by default on a particular {@link Scheduler}.
* * * @param handler - * receives a Publisher of notifications with which a user can complete or error, aborting the + * receives a {@code Publisher} of notifications with which a user can complete or error, aborting the * retry - * @return the source Publisher modified with retry logic + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code handler} is {@code null} * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @@ -13607,42 +14732,42 @@ public final Flowable retryUntil(final BooleanSupplier stop) { @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) public final Flowable retryWhen( - final Function, ? extends Publisher> handler) { - ObjectHelper.requireNonNull(handler, "handler is null"); + @NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { + Objects.requireNonNull(handler, "handler is null"); - return RxJavaPlugins.onAssembly(new FlowableRetryWhen(this, handler)); + return RxJavaPlugins.onAssembly(new FlowableRetryWhen<>(this, handler)); } /** - * Subscribes to the current Flowable and wraps the given Subscriber into a SafeSubscriber - * (if not already a SafeSubscriber) that - * deals with exceptions thrown by a misbehaving Subscriber (that doesn't follow the - * Reactive Streams specification). + * Subscribes to the current {@code Flowable} and wraps the given {@link Subscriber} into a {@link SafeSubscriber} + * (if not already a {@code SafeSubscriber}) that + * deals with exceptions thrown by a misbehaving {@code Subscriber} (that doesn't follow the + * Reactive Streams specification). *
*
Backpressure:
- *
This operator leaves the reactive world and the backpressure behavior depends on the Subscriber's behavior.
+ *
This operator leaves the reactive world and the backpressure behavior depends on the {@code Subscriber}'s behavior.
*
Scheduler:
*
{@code safeSubscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param s the incoming Subscriber instance - * @throws NullPointerException if s is null + * @param subscriber the incoming {@code Subscriber} instance + * @throws NullPointerException if {@code subscriber} is {@code null} */ @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final void safeSubscribe(Subscriber s) { - ObjectHelper.requireNonNull(s, "s is null"); - if (s instanceof SafeSubscriber) { - subscribe((SafeSubscriber)s); + public final void safeSubscribe(@NonNull Subscriber subscriber) { + Objects.requireNonNull(subscriber, "subscriber is null"); + if (subscriber instanceof SafeSubscriber) { + subscribe((SafeSubscriber)subscriber); } else { - subscribe(new SafeSubscriber(s)); + subscribe(new SafeSubscriber<>(subscriber)); } } /** - * Returns a Flowable that emits the most recently emitted item (if any) emitted by the source Publisher + * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} * within periodic time intervals. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -13654,8 +14779,8 @@ public final void safeSubscribe(Subscriber s) { * the sampling rate * @param unit * the {@link TimeUnit} in which {@code period} is defined - * @return a Flowable that emits the results of sampling the items emitted by the source Publisher at - * the specified time interval + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @see #throttleLast(long, TimeUnit) @@ -13663,15 +14788,16 @@ public final void safeSubscribe(Subscriber s) { @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable sample(long period, TimeUnit unit) { + @NonNull + public final Flowable sample(long period, @NonNull TimeUnit unit) { return sample(period, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits the most recently emitted item (if any) emitted by the source Publisher + * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} * within periodic time intervals and optionally emit the very last upstream item when the upstream completes. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -13685,11 +14811,11 @@ public final Flowable sample(long period, TimeUnit unit) { * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param emitLast - * if true and the upstream completes while there is still an unsampled item available, + * if {@code true}, and the upstream completes while there is still an unsampled item available, * that item is emitted to downstream before completion - * if false, an unsampled last item is ignored. - * @return a Flowable that emits the results of sampling the items emitted by the source Publisher at - * the specified time interval + * if {@code false}, an unsampled last item is ignored. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @see #throttleLast(long, TimeUnit) @@ -13698,20 +14824,21 @@ public final Flowable sample(long period, TimeUnit unit) { @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable sample(long period, TimeUnit unit, boolean emitLast) { + @NonNull + public final Flowable sample(long period, @NonNull TimeUnit unit, boolean emitLast) { return sample(period, unit, Schedulers.computation(), emitLast); } /** - * Returns a Flowable that emits the most recently emitted item (if any) emitted by the source Publisher - * within periodic time intervals, where the intervals are defined on a particular Scheduler. + * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} + * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param period @@ -13719,9 +14846,9 @@ public final Flowable sample(long period, TimeUnit unit, boolean emitLast) { * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param scheduler - * the {@link Scheduler} to use when sampling - * @return a Flowable that emits the results of sampling the items emitted by the source Publisher at - * the specified time interval + * the {@code Scheduler} to use when sampling + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @see #throttleLast(long, TimeUnit, Scheduler) @@ -13730,23 +14857,23 @@ public final Flowable sample(long period, TimeUnit unit, boolean emitLast) { @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable sample(long period, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableSampleTimed(this, period, unit, scheduler, false)); + public final Flowable sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableSampleTimed<>(this, period, unit, scheduler, false, null)); } /** - * Returns a Flowable that emits the most recently emitted item (if any) emitted by the source Publisher - * within periodic time intervals, where the intervals are defined on a particular Scheduler + * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} + * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler} * and optionally emit the very last upstream item when the upstream completes. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* *

History: 2.0.5 - experimental @@ -13755,13 +14882,13 @@ public final Flowable sample(long period, TimeUnit unit, Scheduler scheduler) * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param scheduler - * the {@link Scheduler} to use when sampling + * the {@code Scheduler} to use when sampling * @param emitLast - * if true and the upstream completes while there is still an unsampled item available, + * if {@code true} and the upstream completes while there is still an unsampled item available, * that item is emitted to downstream before completion - * if false, an unsampled last item is ignored. - * @return a Flowable that emits the results of sampling the items emitted by the source Publisher at - * the specified time interval + * if {@code false}, an unsampled last item is ignored. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @see #throttleLast(long, TimeUnit, Scheduler) @@ -13771,31 +14898,75 @@ public final Flowable sample(long period, TimeUnit unit, Scheduler scheduler) @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable sample(long period, TimeUnit unit, Scheduler scheduler, boolean emitLast) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableSampleTimed(this, period, unit, scheduler, emitLast)); + public final Flowable sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableSampleTimed<>(this, period, unit, scheduler, emitLast, null)); + } + + /** + * Returns a {@code Flowable} that emits the most recently emitted item (if any) emitted by the current {@code Flowable} + * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler} + * and optionally emit the very last upstream item when the upstream completes. + *

+ * + *

+ *
Backpressure:
+ *
This operator does not support backpressure as it uses time to control data flow.
+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param period + * the sampling rate + * @param unit + * the {@link TimeUnit} in which {@code period} is defined + * @param scheduler + * the {@code Scheduler} to use when sampling + * @param emitLast + * if {@code true} and the upstream completes while there is still an unsampled item available, + * that item is emitted to downstream before completion + * if {@code false}, an unsampled last item is ignored. + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Sample + * @see RxJava wiki: Backpressure + * @see #throttleLast(long, TimeUnit, Scheduler) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.ERROR) + @SchedulerSupport(SchedulerSupport.CUSTOM) + @Experimental + public final Flowable sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new FlowableSampleTimed<>(this, period, unit, scheduler, emitLast, onDropped)); } /** - * Returns a Flowable that, when the specified {@code sampler} Publisher emits an item or completes, - * emits the most recently emitted item (if any) emitted by the source Publisher since the previous - * emission from the {@code sampler} Publisher. + * Returns a {@code Flowable} that, when the specified {@code sampler} {@link Publisher} emits an item or completes, + * emits the most recently emitted item (if any) emitted by the current {@code Flowable} since the previous + * emission from the {@code sampler} {@code Publisher}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses the emissions of the {@code sampler} - * Publisher to control data flow.
+ * {@code Publisher} to control data flow. *
Scheduler:
*
This version of {@code sample} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the sampler Publisher + * @param the element type of the sampler {@code Publisher} * @param sampler - * the Publisher to use for sampling the source Publisher - * @return a Flowable that emits the results of sampling the items emitted by this Publisher whenever - * the {@code sampler} Publisher emits an item or completes + * the {@code Publisher} to use for sampling the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sampler} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure */ @@ -13803,36 +14974,36 @@ public final Flowable sample(long period, TimeUnit unit, Scheduler scheduler, @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable sample(Publisher sampler) { - ObjectHelper.requireNonNull(sampler, "sampler is null"); - return RxJavaPlugins.onAssembly(new FlowableSamplePublisher(this, sampler, false)); + public final <@NonNull U> Flowable sample(@NonNull Publisher sampler) { + Objects.requireNonNull(sampler, "sampler is null"); + return RxJavaPlugins.onAssembly(new FlowableSamplePublisher<>(this, sampler, false)); } /** - * Returns a Flowable that, when the specified {@code sampler} Publisher emits an item or completes, - * emits the most recently emitted item (if any) emitted by the source Publisher since the previous - * emission from the {@code sampler} Publisher - * and optionally emit the very last upstream item when the upstream or other Publisher complete. + * Returns a {@code Flowable} that, when the specified {@code sampler} {@link Publisher} emits an item or completes, + * emits the most recently emitted item (if any) emitted by the current {@code Flowable} since the previous + * emission from the {@code sampler} {@code Publisher} + * and optionally emit the very last upstream item when the upstream or other {@code Publisher} complete. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses the emissions of the {@code sampler} - * Publisher to control data flow.
+ * {@code Publisher} to control data flow. *
Scheduler:
*
This version of {@code sample} does not operate by default on a particular {@link Scheduler}.
*
* *

History: 2.0.5 - experimental - * @param the element type of the sampler Publisher + * @param the element type of the sampler {@code Publisher} * @param sampler - * the Publisher to use for sampling the source Publisher + * the {@code Publisher} to use for sampling the current {@code Flowable} * @param emitLast - * if true and the upstream completes while there is still an unsampled item available, + * if {@code true} and the upstream completes while there is still an unsampled item available, * that item is emitted to downstream before completion - * if false, an unsampled last item is ignored. - * @return a Flowable that emits the results of sampling the items emitted by this Publisher whenever - * the {@code sampler} Publisher emits an item or completes + * if {@code false}, an unsampled last item is ignored. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sampler} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @since 2.1 @@ -13841,58 +15012,57 @@ public final Flowable sample(Publisher sampler) { @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable sample(Publisher sampler, boolean emitLast) { - ObjectHelper.requireNonNull(sampler, "sampler is null"); - return RxJavaPlugins.onAssembly(new FlowableSamplePublisher(this, sampler, emitLast)); + public final <@NonNull U> Flowable sample(@NonNull Publisher sampler, boolean emitLast) { + Objects.requireNonNull(sampler, "sampler is null"); + return RxJavaPlugins.onAssembly(new FlowableSamplePublisher<>(this, sampler, emitLast)); } /** - * Returns a Flowable that applies a specified accumulator function to the first item emitted by a source - * Publisher, then feeds the result of that function along with the second item emitted by the source - * Publisher into the same function, and so on until all items have been emitted by the source Publisher, - * emitting the result of each of these iterations. + * Returns a {@code Flowable} that emits the first value emitted by the current {@code Flowable}, then emits one value + * for each subsequent value emitted by the current {@code Flowable}. Each emission after the first is the result of + * applying the specified accumulator function to the previous emission and the corresponding value from the current {@code Flowable}. *

- * + * *

* This sort of function is sometimes called an accumulator. *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * Violating this expectation, a {@code MissingBackpressureException} may get signaled somewhere downstream.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream.
*
Scheduler:
*
{@code scan} does not operate by default on a particular {@link Scheduler}.
*
* * @param accumulator - * an accumulator function to be invoked on each item emitted by the source Publisher, whose + * an accumulator function to be invoked on each item emitted by the current {@code Flowable}, whose * result will be emitted to {@link Subscriber}s via {@link Subscriber#onNext onNext} and used in the * next accumulator call - * @return a Flowable that emits the results of each call to the accumulator function + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code accumulator} is {@code null} * @see ReactiveX operators documentation: Scan */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable scan(BiFunction accumulator) { - ObjectHelper.requireNonNull(accumulator, "accumulator is null"); - return RxJavaPlugins.onAssembly(new FlowableScan(this, accumulator)); + public final Flowable scan(@NonNull BiFunction accumulator) { + Objects.requireNonNull(accumulator, "accumulator is null"); + return RxJavaPlugins.onAssembly(new FlowableScan<>(this, accumulator)); } /** - * Returns a Flowable that applies a specified accumulator function to the first item emitted by a source - * Publisher and a seed value, then feeds the result of that function along with the second item emitted by - * the source Publisher into the same function, and so on until all items have been emitted by the source - * Publisher, emitting the result of each of these iterations. + * Returns a {@code Flowable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Flowable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current {@code Flowable}. *

- * + * *

* This sort of function is sometimes called an accumulator. *

- * Note that the Publisher that results from this method will emit {@code initialValue} as its first + * Note that the {@code Flowable} that results from this method will emit {@code initialValue} as its first * emitted item. *

- * Note that the {@code initialValue} is shared among all subscribers to the resulting Publisher + * Note that the {@code initialValue} is shared among all subscribers to the resulting {@code Flowable} * and may cause problems if it is mutable. To make sure each subscriber gets its own value, defer * the application of this operator via {@link #defer(Supplier)}: *


@@ -13907,8 +15077,10 @@ public final Flowable scan(BiFunction accumulator) {
      * 
*
*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * Violating this expectation, a {@code MissingBackpressureException} may get signaled somewhere downstream.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream. + * The downstream request pattern is not preserved across this operator. + * The upstream is requested {@link #bufferSize()} - 1 upfront and 75% of {@link #bufferSize()} thereafter.
*
Scheduler:
*
{@code scan} does not operate by default on a particular {@link Scheduler}.
*
@@ -13917,207 +15089,210 @@ public final Flowable scan(BiFunction accumulator) { * @param initialValue * the initial (seed) accumulator item * @param accumulator - * an accumulator function to be invoked on each item emitted by the source Publisher, whose + * an accumulator function to be invoked on each item emitted by the current {@code Flowable}, whose * result will be emitted to {@link Subscriber}s via {@link Subscriber#onNext onNext} and used in the * next accumulator call - * @return a Flowable that emits {@code initialValue} followed by the results of each call to the - * accumulator function + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code initialValue} or {@code accumulator} is {@code null} * @see ReactiveX operators documentation: Scan */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable scan(final R initialValue, BiFunction accumulator) { - ObjectHelper.requireNonNull(initialValue, "initialValue is null"); + public final <@NonNull R> Flowable scan(R initialValue, @NonNull BiFunction accumulator) { + Objects.requireNonNull(initialValue, "initialValue is null"); return scanWith(Functions.justSupplier(initialValue), accumulator); } /** - * Returns a Flowable that applies a specified accumulator function to the first item emitted by a source - * Publisher and a seed value, then feeds the result of that function along with the second item emitted by - * the source Publisher into the same function, and so on until all items have been emitted by the source - * Publisher, emitting the result of each of these iterations. + * Returns a {@code Flowable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Flowable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current {@code Flowable}. *

- * + * *

* This sort of function is sometimes called an accumulator. *

- * Note that the Publisher that results from this method will emit the value returned by + * Note that the {@code Flowable} that results from this method will emit the value returned by * the {@code seedSupplier} as its first item. *

*
Backpressure:
- *
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * Violating this expectation, a {@code MissingBackpressureException} may get signaled somewhere downstream.
+ *
The operator honors downstream backpressure and expects the current {@code Flowable} to honor backpressure as well. + * Violating this expectation, a {@link MissingBackpressureException} may get signaled somewhere downstream. + * The downstream request pattern is not preserved across this operator. + * The upstream is requested {@link #bufferSize()} - 1 upfront and 75% of {@link #bufferSize()} thereafter.
*
Scheduler:
*
{@code scanWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param the initial, accumulator and result type * @param seedSupplier - * a Supplier that returns the initial (seed) accumulator item for each individual Subscriber + * a {@link Supplier} that returns the initial (seed) accumulator item for each individual {@link Subscriber} * @param accumulator - * an accumulator function to be invoked on each item emitted by the source Publisher, whose - * result will be emitted to {@link Subscriber}s via {@link Subscriber#onNext onNext} and used in the + * an accumulator function to be invoked on each item emitted by the current {@code Flowable}, whose + * result will be emitted to {@code Subscriber}s via {@link Subscriber#onNext onNext} and used in the * next accumulator call - * @return a Flowable that emits {@code initialValue} followed by the results of each call to the - * accumulator function + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code seedSupplier} or {@code accumulator} is {@code null} * @see ReactiveX operators documentation: Scan */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable scanWith(Supplier seedSupplier, BiFunction accumulator) { - ObjectHelper.requireNonNull(seedSupplier, "seedSupplier is null"); - ObjectHelper.requireNonNull(accumulator, "accumulator is null"); - return RxJavaPlugins.onAssembly(new FlowableScanSeed(this, seedSupplier, accumulator)); + public final <@NonNull R> Flowable scanWith(@NonNull Supplier seedSupplier, @NonNull BiFunction accumulator) { + Objects.requireNonNull(seedSupplier, "seedSupplier is null"); + Objects.requireNonNull(accumulator, "accumulator is null"); + return RxJavaPlugins.onAssembly(new FlowableScanSeed<>(this, seedSupplier, accumulator)); } /** - * Forces a Publisher's emissions and notifications to be serialized and for it to obey - * the Publisher contract in other ways. + * Forces the current {@code Flowable}'s emissions and notifications to be serialized and for it to obey + * the {@code Publisher} contract in other ways. *

- * It is possible for a Publisher to invoke its Subscribers' methods asynchronously, perhaps from - * different threads. This could make such a Publisher poorly-behaved, in that it might try to invoke + * It is possible for a {@link Publisher} to invoke its {@link Subscriber}s' methods asynchronously, perhaps from + * different threads. This could make such a {@code Publisher} poorly-behaved, in that it might try to invoke * {@code onComplete} or {@code onError} before one of its {@code onNext} invocations, or it might call - * {@code onNext} from two different threads concurrently. You can force such a Publisher to be + * {@code onNext} from two different threads concurrently. You can force such a {@code Publisher} to be * well-behaved and sequential by applying the {@code serialize} method to it. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code serialize} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Publisher} that is guaranteed to be well-behaved and to make only serialized calls to - * its Subscribers + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Serialize */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable serialize() { - return RxJavaPlugins.onAssembly(new FlowableSerialized(this)); + return RxJavaPlugins.onAssembly(new FlowableSerialized<>(this)); } /** - * Returns a new {@link Publisher} that multicasts (and shares a single subscription to) the original {@link Publisher}. As long as - * there is at least one {@link Subscriber} this {@link Publisher} will be subscribed and emitting data. - * When all subscribers have canceled it will cancel the source {@link Publisher}. + * Returns a new {@code Flowable} that multicasts (and shares a single subscription to) the current {@code Flowable}. As long as + * there is at least one {@link Subscriber}, the current {@code Flowable} will be subscribed and emitting data. + * When all subscribers have canceled it will cancel the current {@code Flowable}. *

* This is an alias for {@link #publish()}.{@link ConnectableFlowable#refCount() refCount()}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator will signal a {@code MissingBackpressureException} to + *
The operator honors backpressure and expects the current {@code Flowable} to honor backpressure as well. + * If this expectation is violated, the operator will signal a {@link MissingBackpressureException} to * its {@code Subscriber}s.
*
Scheduler:
*
{@code share} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@code Publisher} that upon connection causes the source {@code Publisher} to emit items - * to its {@link Subscriber}s + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: RefCount */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable share() { return publish().refCount(); } /** - * Returns a Maybe that completes if this Flowable is empty, signals one item if this Flowable - * signals exactly one item or signals an {@code IllegalArgumentException} if this Flowable signals + * Returns a {@link Maybe} that completes if this {@code Flowable} is empty, signals one item if this {@code Flowable} + * signals exactly one item or signals an {@link IllegalArgumentException} if this {@code Flowable} signals * more than one item. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code singleElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Maybe that emits the single item emitted by the source Publisher + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe singleElement() { - return RxJavaPlugins.onAssembly(new FlowableSingleMaybe(this)); + return RxJavaPlugins.onAssembly(new FlowableSingleMaybe<>(this)); } /** - * Returns a Single that emits the single item emitted by the source Publisher, if that Publisher - * emits only a single item, or a default item if the source Publisher emits no items. If the source - * Publisher emits more than one item, an {@code IllegalArgumentException} is signaled instead. + * Returns a {@link Single} that emits the single item emitted by the current {@code Flowable} if it + * emits only a single item, or a default item if the current {@code Flowable} emits no items. If the current + * {@code Flowable} emits more than one item, an {@link IllegalArgumentException} is signaled instead. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code single} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * a default value to emit if the source Publisher emits no item - * @return a Single that emits the single item emitted by the source Publisher, or a default item if - * the source Publisher is empty + * a default value to emit if the current {@code Flowable} emits no item + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: First */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single single(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); - return RxJavaPlugins.onAssembly(new FlowableSingleSingle(this, defaultItem)); + public final Single single(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new FlowableSingleSingle<>(this, defaultItem)); } /** - * Returns a Single that emits the single item emitted by this Flowable, if this Flowable + * Returns a {@link Single} that emits the single item emitted by this {@code Flowable}, if this {@code Flowable} * emits only a single item, otherwise - * if this Flowable completes without emitting any items a {@link NoSuchElementException} will be signaled and - * if this Flowable emits more than one item, an {@code IllegalArgumentException} will be signaled. + * if this {@code Flowable} completes without emitting any items a {@link NoSuchElementException} will be signaled and + * if this {@code Flowable} emits more than one item, an {@link IllegalArgumentException} will be signaled. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure).
*
Scheduler:
*
{@code singleOrError} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Single instance + * @return the new {@code Single} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single singleOrError() { - return RxJavaPlugins.onAssembly(new FlowableSingleSingle(this, null)); + return RxJavaPlugins.onAssembly(new FlowableSingleSingle<>(this, null)); } /** - * Returns a Flowable that skips the first {@code count} items emitted by the source Publisher and emits + * Returns a {@code Flowable} that skips the first {@code count} items emitted by the current {@code Flowable} and emits * the remainder. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
This version of {@code skip} does not operate by default on a particular {@link Scheduler}.
@@ -14125,29 +15300,33 @@ public final Single singleOrError() { * * @param count * the number of items to skip - * @return a Flowable that is identical to the source Publisher except that it does not emit the first - * {@code count} items that the source Publisher emits + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code count} is negative * @see ReactiveX operators documentation: Skip */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable skip(long count) { - if (count <= 0L) { + if (count < 0) { + throw new IllegalArgumentException("count >= 0 expected but it was " + count); + } + if (count == 0) { return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new FlowableSkip(this, count)); + return RxJavaPlugins.onAssembly(new FlowableSkip<>(this, count)); } /** - * Returns a Flowable that skips values emitted by the source Publisher before a specified time window + * Returns a {@code Flowable} that skips values emitted by the current {@code Flowable} before a specified time window * elapses. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
*
{@code skip} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
@@ -14157,28 +15336,29 @@ public final Flowable skip(long count) { * the length of the time window to skip * @param unit * the time unit of {@code time} - * @return a Flowable that skips values emitted by the source Publisher before the time window defined - * by {@code time} elapses and the emits the remainder + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Skip */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable skip(long time, TimeUnit unit) { + @NonNull + public final Flowable skip(long time, @NonNull TimeUnit unit) { return skipUntil(timer(time, unit)); } /** - * Returns a Flowable that skips values emitted by the source Publisher before a specified time window + * Returns a {@code Flowable} that skips values emitted by the current {@code Flowable} before a specified time window * on a specified {@link Scheduler} elapses. *

- * + * *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
- *
You specify which {@link Scheduler} this operator will use for the timed skipping
+ *
You specify which {@code Scheduler} this operator will use for the timed skipping
*
* * @param time @@ -14186,30 +15366,31 @@ public final Flowable skip(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the {@link Scheduler} on which the timed wait happens - * @return a Flowable that skips values emitted by the source Publisher before the time window defined - * by {@code time} and {@code scheduler} elapses, and then emits the remainder + * the {@code Scheduler} on which the timed wait happens + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Skip */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable skip(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable skip(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return skipUntil(timer(time, unit, scheduler)); } /** - * Returns a Flowable that drops a specified number of items from the end of the sequence emitted by the - * source Publisher. + * Returns a {@code Flowable} that drops a specified number of items from the end of the sequence emitted by the + * current {@code Flowable}. *

- * + * *

- * This Subscriber accumulates a queue long enough to store the first {@code count} items. As more items are - * received, items are taken from the front of the queue and emitted by the returned Publisher. This causes + * This {@link Subscriber} accumulates a queue long enough to store the first {@code count} items. As more items are + * received, items are taken from the front of the queue and emitted by the resulting {@code Flowable}. This causes * such items to be delayed. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
This version of {@code skipLast} does not operate by default on a particular {@link Scheduler}.
@@ -14217,36 +15398,36 @@ public final Flowable skip(long time, TimeUnit unit, Scheduler scheduler) { * * @param count * number of items to drop from the end of the source sequence - * @return a Flowable that emits the items emitted by the source Publisher except for the dropped ones - * at the end - * @throws IndexOutOfBoundsException + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException * if {@code count} is less than zero * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable skipLast(int count) { if (count < 0) { - throw new IndexOutOfBoundsException("count >= 0 required but it was " + count); + throw new IllegalArgumentException("count >= 0 required but it was " + count); } if (count == 0) { return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new FlowableSkipLast(this, count)); + return RxJavaPlugins.onAssembly(new FlowableSkipLast<>(this, count)); } /** - * Returns a Flowable that drops items emitted by the source Publisher during a specified time window + * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
*
{@code skipLast} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
@@ -14256,28 +15437,29 @@ public final Flowable skipLast(int count) { * the length of the time window * @param unit * the time unit of {@code time} - * @return a Flowable that drops those items emitted by the source Publisher in a time window before the - * source completes defined by {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable skipLast(long time, TimeUnit unit) { + @NonNull + public final Flowable skipLast(long time, @NonNull TimeUnit unit) { return skipLast(time, unit, Schedulers.computation(), false, bufferSize()); } /** - * Returns a Flowable that drops items emitted by the source Publisher during a specified time window + * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
*
{@code skipLast} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
@@ -14288,30 +15470,31 @@ public final Flowable skipLast(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped - * @return a Flowable that drops those items emitted by the source Publisher in a time window before the - * source completes defined by {@code time} + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable skipLast(long time, TimeUnit unit, boolean delayError) { + @NonNull + public final Flowable skipLast(long time, @NonNull TimeUnit unit, boolean delayError) { return skipLast(time, unit, Schedulers.computation(), delayError, bufferSize()); } /** - * Returns a Flowable that drops items emitted by the source Publisher during a specified time window + * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
*
You specify which {@link Scheduler} this operator will use for tracking the current time
*
@@ -14322,28 +15505,29 @@ public final Flowable skipLast(long time, TimeUnit unit, boolean delayError) * the time unit of {@code time} * @param scheduler * the scheduler used as the time source - * @return a Flowable that drops those items emitted by the source Publisher in a time window before the - * source completes defined by {@code time} and {@code scheduler} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable skipLast(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable skipLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return skipLast(time, unit, scheduler, false, bufferSize()); } /** - * Returns a Flowable that drops items emitted by the source Publisher during a specified time window + * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
*
You specify which {@link Scheduler} this operator will use to track the current time
*
@@ -14355,30 +15539,31 @@ public final Flowable skipLast(long time, TimeUnit unit, Scheduler scheduler) * @param scheduler * the scheduler used as the time source * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped - * @return a Flowable that drops those items emitted by the source Publisher in a time window before the - * source completes defined by {@code time} and {@code scheduler} + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable skipLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError) { + @NonNull + public final Flowable skipLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { return skipLast(time, unit, scheduler, delayError, bufferSize()); } /** - * Returns a Flowable that drops items emitted by the source Publisher during a specified time window + * Returns a {@code Flowable} that drops items emitted by the current {@code Flowable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

*
Backpressure:
*
The operator doesn't support backpressure as it uses time to skip an arbitrary number of elements and - * thus has to consume the source {@code Publisher} in an unbounded manner (i.e., no backpressure applied to it).
+ * thus has to consume the current {@code Flowable} in an unbounded manner (i.e., no backpressure applied to it). *
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
@@ -14390,114 +15575,116 @@ public final Flowable skipLast(long time, TimeUnit unit, Scheduler scheduler, * @param scheduler * the scheduler used as the time source * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped * @param bufferSize * the hint about how many elements to expect to be skipped - * @return a Flowable that drops those items emitted by the source Publisher in a time window before the - * source completes defined by {@code time} and {@code scheduler} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable skipLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Flowable skipLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError, int bufferSize) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); // the internal buffer holds pairs of (timestamp, value) so double the default buffer size int s = bufferSize << 1; - return RxJavaPlugins.onAssembly(new FlowableSkipLastTimed(this, time, unit, scheduler, s, delayError)); + return RxJavaPlugins.onAssembly(new FlowableSkipLastTimed<>(this, time, unit, scheduler, s, delayError)); } /** - * Returns a Flowable that skips items emitted by the source Publisher until a second Publisher emits + * Returns a {@code Flowable} that skips items emitted by the current {@code Flowable} until a second {@link Publisher} emits * an item. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code skipUntil} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the other Publisher + * @param the element type of the other {@code Publisher} * @param other - * the second Publisher that has to emit an item before the source Publisher's elements begin - * to be mirrored by the resulting Publisher - * @return a Flowable that skips items from the source Publisher until the second Publisher emits an - * item, then emits the remaining items + * the second {@code Publisher} that has to emit an item before the current {@code Flowable}'s elements begin + * to be mirrored by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: SkipUntil */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable skipUntil(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableSkipUntil(this, other)); + public final <@NonNull U> Flowable skipUntil(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableSkipUntil<>(this, other)); } /** - * Returns a Flowable that skips all items emitted by the source Publisher as long as a specified - * condition holds true, but emits all further source items as soon as the condition becomes false. + * Returns a {@code Flowable} that skips all items emitted by the current {@code Flowable} as long as a specified + * condition holds {@code true}, but emits all further source items as soon as the condition becomes {@code false}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code skipWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function to test each item emitted from the source Publisher - * @return a Flowable that begins emitting items emitted by the source Publisher when the specified - * predicate becomes false + * a function to test each item emitted from the current {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: SkipWhile */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable skipWhile(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableSkipWhile(this, predicate)); + public final Flowable skipWhile(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new FlowableSkipWhile<>(this, predicate)); } /** - * Returns a Flowable that emits the events emitted by source Publisher, in a - * sorted order. Each item emitted by the Publisher must implement {@link Comparable} with respect to all + * Returns a {@code Flowable} that emits the events emitted by source {@link Publisher}, in a + * sorted order. Each item emitted by the {@code Publisher} must implement {@link Comparable} with respect to all * other items in the sequence. * - *

If any item emitted by this Flowable does not implement {@link Comparable} with respect to - * all other items emitted by this Flowable, no items will be emitted and the + *

If any item emitted by this {@code Flowable} does not implement {@code Comparable} with respect to + * all other items emitted by this {@code Flowable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

Note that calling {@code sorted} with long, non-terminating or infinite sources * might cause {@link OutOfMemoryError} * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code sorted} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits the items emitted by the source Publisher in sorted order + * @return the new {@code Flowable} instance */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable sorted() { - return toList().toFlowable().map(Functions.listSorter(Functions.naturalComparator())).flatMapIterable(Functions.>identity()); + return toList().toFlowable().map(Functions.listSorter(Functions.naturalComparator())).flatMapIterable(Functions.identity()); } /** - * Returns a Flowable that emits the events emitted by source Publisher, in a + * Returns a {@code Flowable} that emits the events emitted by source {@link Publisher}, in a * sorted order based on a specified comparison function. * *

Note that calling {@code sorted} with long, non-terminating or infinite sources @@ -14505,147 +15692,222 @@ public final Flowable sorted() { * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code sorted} does not operate by default on a particular {@link Scheduler}.
*
* - * @param sortFunction - * a function that compares two items emitted by the source Publisher and returns an Integer + * @param comparator + * a function that compares two items emitted by the current {@code Flowable} and returns an {@link Integer} * that indicates their sort order - * @return a Flowable that emits the items emitted by the source Publisher in sorted order + * @throws NullPointerException if {@code comparator} is {@code null} + * @return the new {@code Flowable} instance */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable sorted(Comparator sortFunction) { - ObjectHelper.requireNonNull(sortFunction, "sortFunction"); - return toList().toFlowable().map(Functions.listSorter(sortFunction)).flatMapIterable(Functions.>identity()); + public final Flowable sorted(@NonNull Comparator<@NonNull ? super T> comparator) { + Objects.requireNonNull(comparator, "comparator is null"); + return toList().toFlowable().map(Functions.listSorter(comparator)).flatMapIterable(Functions.identity()); } /** - * Returns a Flowable that emits the items in a specified {@link Iterable} before it begins to emit items - * emitted by the source Publisher. + * Returns a {@code Flowable} that emits the items in a specified {@link Iterable} before it begins to emit items + * emitted by the current {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher} + *
The operator honors backpressure from downstream. The Current {@code Flowable} * is expected to honor backpressure as well. If it violates this rule, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when the current {@code Flowable} completes. *
Scheduler:
*
{@code startWithIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param items - * an Iterable that contains the items you want the modified Publisher to emit first - * @return a Flowable that emits the items in the specified {@link Iterable} and then emits the items - * emitted by the source Publisher + * an {@code Iterable} that contains the items you want the resulting {@code Flowable} to emit first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code items} is {@code null} * @see ReactiveX operators documentation: StartWith * @see #startWithArray(Object...) * @see #startWithItem(Object) * @since 3.0.0 */ - @SuppressWarnings("unchecked") @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable startWithIterable(Iterable items) { + @NonNull + public final Flowable startWithIterable(@NonNull Iterable items) { return concatArray(fromIterable(items), this); } /** - * Returns a Flowable that emits the items in a specified {@link Publisher} before it begins to emit - * items emitted by the source Publisher. + * Returns a {@code Flowable} which first runs the other {@link CompletableSource} + * then the current {@code Flowable} if the other completed normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code CompletableSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Completable.wrap(other).toFlowable(), this); + } + + /** + * Returns a {@code Flowable} which first runs the other {@link SingleSource} + * then the current {@code Flowable} if the other succeeded normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code SingleSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Single.wrap(other).toFlowable(), this); + } + + /** + * Returns a {@code Flowable} which first runs the other {@link MaybeSource} + * then the current {@code Flowable} if the other succeeded or completed normally. *

- * + * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code MaybeSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Maybe.wrap(other).toFlowable(), this); + } + + /** + * Returns a {@code Flowable} that emits the items in a specified {@link Publisher} before it begins to emit + * items emitted by the current {@code Flowable}. + *

+ * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the {@code other} {@code Publisher}s * are expected to honor backpressure as well. If any of then violates this rule, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when the current {@code Flowable} completes. *
Scheduler:
*
{@code startWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * a Publisher that contains the items you want the modified Publisher to emit first - * @return a Flowable that emits the items in the specified {@link Publisher} and then emits the items - * emitted by the source Publisher + * a {@code Publisher} that contains the items you want the modified {@code Publisher} to emit first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: StartWith */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable startWith(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Flowable startWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); return concatArray(other, this); } /** - * Returns a Flowable that emits a specified item before it begins to emit items emitted by the source - * Publisher. + * Returns a {@code Flowable} that emits a specified item before it begins to emit items emitted by the current + * {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher} + *
The operator honors backpressure from downstream. The current {@code Flowable} * is expected to honor backpressure as well. If it violates this rule, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when the current {@code Flowable} completes. *
Scheduler:
*
{@code startWithItem} does not operate by default on a particular {@link Scheduler}.
*
* * @param item * the item to emit first - * @return a Flowable that emits the specified item before it begins to emit items emitted by the source - * Publisher + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: StartWith * @see #startWithArray(Object...) * @see #startWithIterable(Iterable) * @since 3.0.0 */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable startWithItem(T item) { - ObjectHelper.requireNonNull(item, "item is null"); + public final Flowable startWithItem(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); return concatArray(just(item), this); } /** - * Returns a Flowable that emits the specified items before it begins to emit items emitted by the source - * Publisher. + * Returns a {@code Flowable} that emits the specified items before it begins to emit items emitted by the current + * {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream. The source {@code Publisher} + *
The operator honors backpressure from downstream. The current {@code Flowable} * is expected to honor backpressure as well. If it violates this rule, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * {@link IllegalStateException} when the current {@code Flowable} completes. *
Scheduler:
*
{@code startWithArray} does not operate by default on a particular {@link Scheduler}.
*
* * @param items * the array of values to emit first - * @return a Flowable that emits the specified items before it begins to emit items emitted by the source - * Publisher + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code items} is {@code null} * @see ReactiveX operators documentation: StartWith * @see #startWithItem(Object) * @see #startWithIterable(Iterable) */ - @SuppressWarnings("unchecked") @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable startWithArray(T... items) { + @SafeVarargs + @NonNull + public final Flowable startWithArray(@NonNull T... items) { Flowable fromArray = fromArray(items); if (fromArray == empty()) { return RxJavaPlugins.onAssembly(this); @@ -14654,169 +15916,215 @@ public final Flowable startWithArray(T... items) { } /** - * Subscribes to a Publisher and ignores {@code onNext} and {@code onComplete} emissions. + * Subscribes to the current {@code Flowable} and ignores {@code onNext} and {@code onComplete} emissions. *

- * If the Flowable emits an error, it is wrapped into an + * If the {@code Flowable} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Publisher has finished sending them + * @return the new {@link Disposable} instance that allows cancelling the flow * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Disposable subscribe() { return subscribe(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to a Publisher and provides a callback to handle the items it emits. + * Subscribes to the current {@code Flowable} and provides a callback to handle the items it emits. *

- * If the Flowable emits an error, it is wrapped into an + * If the {@code Flowable} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the {@code Consumer} you have designed to accept emissions from the Publisher - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Publisher has finished sending them + * the {@code Consumer} you have designed to accept emissions from the current {@code Flowable} + * @return the new {@link Disposable} instance that allows cancelling the flow * @throws NullPointerException - * if {@code onNext} is null + * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onNext) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onNext) { return subscribe(onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to a Publisher and provides callbacks to handle the items it emits and any error + * Subscribes to the current {@code Flowable} and provides callbacks to handle the items it emits and any error * notification it issues. *
*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the {@code Consumer} you have designed to accept emissions from the Publisher + * the {@code Consumer} you have designed to accept emissions from the current {@code Flowable} * @param onError * the {@code Consumer} you have designed to accept any error notification from the - * Publisher - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Publisher has finished sending them - * @see ReactiveX operators documentation: Subscribe + * current {@code Flowable} + * @return the new {@link Disposable} instance that allows cancelling the flow * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null + * if {@code onNext} or {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onNext, Consumer onError) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onNext, @NonNull Consumer onError) { return subscribe(onNext, onError, Functions.EMPTY_ACTION); } /** - * Subscribes to a Publisher and provides callbacks to handle the items it emits and any error or + * Subscribes to the current {@code Flowable} and provides callbacks to handle the items it emits and any error or * completion notification it issues. *
*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner (i.e., no + *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no * backpressure is applied to it).
*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the {@code Consumer} you have designed to accept emissions from the Publisher + * the {@code Consumer} you have designed to accept emissions from the current {@code Flowable} * @param onError * the {@code Consumer} you have designed to accept any error notification from the - * Publisher + * current {@code Flowable} * @param onComplete - * the {@code Action} you have designed to accept a completion notification from the - * Publisher - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Publisher has finished sending them + * the {@link Action} you have designed to accept a completion notification from the + * the current {@code Flowable} + * @return the new {@link Disposable} instance that allows cancelling the flow * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null, or - * if {@code onComplete} is null + * if {@code onNext}, {@code onError} or {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onNext, Consumer onError, - Action onComplete) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + @NonNull + public final Disposable subscribe(@NonNull Consumer onNext, @NonNull Consumer onError, + @NonNull Action onComplete) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); - LambdaSubscriber ls = new LambdaSubscriber(onNext, onError, onComplete, FlowableInternalHelper.RequestMax.INSTANCE); + LambdaSubscriber ls = new LambdaSubscriber<>(onNext, onError, onComplete, FlowableInternalHelper.RequestMax.INSTANCE); subscribe(ls); return ls; } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link Subscriber}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code Subscriber} is removed + * from the given container. + *

+ * The {@code Subscriber} will be removed after the callback for the terminal event has been invoked. + *

+ *
Backpressure:
+ *
The operator consumes the current {@code Flowable} in an unbounded manner (i.e., no + * backpressure is applied to it).
+ *
Scheduler:
+ *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param onNext the callback for upstream items + * @param onError the callback for an upstream error if any + * @param onComplete the callback for the upstream completion if any + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code Subscriber} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onNext}, {@code onError}, + * {@code onComplete} or {@code container} is {@code null} + * @since 3.1.0 + */ + @BackpressureSupport(BackpressureKind.SPECIAL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onNext, + @NonNull Consumer onError, + @NonNull Action onComplete, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseSubscriber subscriber = new DisposableAutoReleaseSubscriber<>( + container, onNext, onError, onComplete); + container.add(subscriber); + subscribe(subscriber); + return subscriber; + } + @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) @Override - public final void subscribe(Subscriber s) { - if (s instanceof FlowableSubscriber) { - subscribe((FlowableSubscriber)s); + public final void subscribe(@NonNull Subscriber subscriber) { + if (subscriber instanceof FlowableSubscriber) { + subscribe((FlowableSubscriber)subscriber); } else { - ObjectHelper.requireNonNull(s, "s is null"); - subscribe(new StrictSubscriber(s)); + Objects.requireNonNull(subscriber, "subscriber is null"); + subscribe(new StrictSubscriber<>(subscriber)); } } /** - * Establish a connection between this Flowable and the given FlowableSubscriber and - * start streaming events based on the demand of the FlowableSubscriber. + * Establish a connection between this {@code Flowable} and the given {@link FlowableSubscriber} and + * start streaming events based on the demand of the {@code FlowableSubscriber}. *

* This is a "factory method" and can be called multiple times, each time starting a new {@link Subscription}. *

- * Each {@link Subscription} will work for only a single {@link FlowableSubscriber}. + * Each {@code Subscription} will work for only a single {@code FlowableSubscriber}. *

- * If the same {@link FlowableSubscriber} instance is subscribed to multiple {@link Flowable}s and/or the - * same {@link Flowable} multiple times, it must ensure the serialization over its {@code onXXX} + * If the same {@code FlowableSubscriber} instance is subscribed to multiple {@code Flowable}s and/or the + * same {@code Flowable} multiple times, it must ensure the serialization over its {@code onXXX} * methods manually. *

- * If the {@link Flowable} rejects the subscription attempt or otherwise fails it will signal + * If the {@code Flowable} rejects the subscription attempt or otherwise fails it will signal * the error via {@link FlowableSubscriber#onError(Throwable)}. *

- * This subscribe method relaxes the following Reactive Streams rules: + * This subscribe method relaxes the following Reactive Streams rules: *

    - *
  • §1.3: onNext should not be called concurrently until onSubscribe returns. - * FlowableSubscriber.onSubscribe should make sure a sync or async call triggered by request() is safe.
  • - *
  • §2.3: onError or onComplete must not call cancel. + *
  • §1.3: {@code onNext} should not be called concurrently until {@code onSubscribe} returns. + * {@link FlowableSubscriber#onSubscribe(Subscription)} should make sure a sync or async call triggered by request() is safe.
  • + *
  • §2.3: {@code onError} or {@code onComplete} must not call cancel. * Calling request() or cancel() is NOP at this point.
  • - *
  • §2.12: onSubscribe must be called at most once on the same instance. - * FlowableSubscriber reuse is not checked and if happens, it is the responsibility of - * the FlowableSubscriber to ensure proper serialization of its onXXX methods.
  • - *
  • §3.9: negative requests should emit an onError(IllegalArgumentException). - * Non-positive requests signal via RxJavaPlugins.onError and the stream is not affected.
  • + *
  • §2.12: {@code onSubscribe} must be called at most once on the same instance. + * {@code FlowableSubscriber} reuse is not checked and if happens, it is the responsibility of + * the {@code FlowableSubscriber} to ensure proper serialization of its onXXX methods.
  • + *
  • §3.9: negative requests should emit an {@code onError(IllegalArgumentException)}. + * Non-positive requests signal via {@link RxJavaPlugins#onError(Throwable)} and the stream is not affected.
  • *
*
*
Backpressure:
@@ -14825,19 +16133,20 @@ public final void subscribe(Subscriber s) { *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.7 - experimental; 2.1 - beta - * @param s the FlowableSubscriber that will consume signals from this Flowable + * @param subscriber the {@code FlowableSubscriber} that will consume signals from this {@code Flowable} + * @throws NullPointerException if {@code subscriber} is {@code null} * @since 2.2 */ @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final void subscribe(FlowableSubscriber s) { - ObjectHelper.requireNonNull(s, "s is null"); + public final void subscribe(@NonNull FlowableSubscriber subscriber) { + Objects.requireNonNull(subscriber, "subscriber is null"); try { - Subscriber z = RxJavaPlugins.onSubscribe(this, s); + Subscriber flowableSubscriber = RxJavaPlugins.onSubscribe(this, subscriber); - ObjectHelper.requireNonNull(z, "The RxJavaPlugins.onSubscribe hook returned a null FlowableSubscriber. Please check the handler provided to RxJavaPlugins.setOnFlowableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); + Objects.requireNonNull(flowableSubscriber, "The RxJavaPlugins.onSubscribe hook returned a null FlowableSubscriber. Please check the handler provided to RxJavaPlugins.setOnFlowableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); - subscribeActual(z); + subscribeActual(flowableSubscriber); } catch (NullPointerException e) { // NOPMD throw e; } catch (Throwable e) { @@ -14858,13 +16167,13 @@ public final void subscribe(FlowableSubscriber s) { *

There is no need to call any of the plugin hooks on the current {@code Flowable} instance or * the {@code Subscriber}; all hooks and basic safeguards have been * applied by {@link #subscribe(Subscriber)} before this method gets called. - * @param s the incoming Subscriber, never null + * @param subscriber the incoming {@code Subscriber}, never {@code null} */ - protected abstract void subscribeActual(Subscriber s); + protected abstract void subscribeActual(@NonNull Subscriber subscriber); /** - * Subscribes a given Subscriber (subclass) to this Flowable and returns the given - * Subscriber as is. + * Subscribes a given {@link Subscriber} (subclass) to this {@code Flowable} and returns the given + * {@code Subscriber} as is. *

Usage example: *


      * Flowable<Integer> source = Flowable.range(1, 10);
@@ -14883,40 +16192,41 @@ public final void subscribe(FlowableSubscriber s) {
      *  
Scheduler:
*
{@code subscribeWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param the type of the Subscriber to use and return - * @param subscriber the Subscriber (subclass) to use and return, not null + * @param the type of the {@code Subscriber} to use and return + * @param subscriber the {@code Subscriber} (subclass) to use and return, not {@code null} * @return the input {@code subscriber} - * @throws NullPointerException if {@code subscriber} is null + * @throws NullPointerException if {@code subscriber} is {@code null} * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final > E subscribeWith(E subscriber) { + @NonNull + public final <@NonNull E extends Subscriber> E subscribeWith(E subscriber) { subscribe(subscriber); return subscriber; } /** - * Asynchronously subscribes Subscribers to this Publisher on the specified {@link Scheduler}. + * Asynchronously subscribes {@link Subscriber}s to the current {@code Flowable} on the specified {@link Scheduler}. *

* If there is a {@link #create(FlowableOnSubscribe, BackpressureStrategy)} type source up in the * chain, it is recommended to use {@code subscribeOn(scheduler, false)} instead * to avoid same-pool deadlock because requests may pile up behind an eager/blocking emitter. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to perform subscription actions on - * @return the source Publisher modified so that its subscriptions happen on the - * specified {@link Scheduler} + * the {@code Scheduler} to perform subscription actions on + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn * @see RxJava Threading Examples * @see #observeOn @@ -14927,34 +16237,34 @@ public final > E subscribeWith(E subscriber) { @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.CUSTOM) public final Flowable subscribeOn(@NonNull Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return subscribeOn(scheduler, !(this instanceof FlowableCreate)); } /** - * Asynchronously subscribes Subscribers to this Publisher on the specified {@link Scheduler} - * optionally reroutes requests from other threads to the same {@link Scheduler} thread. + * Asynchronously subscribes {@link Subscriber}s to the current {@code Flowable} on the specified {@link Scheduler} + * optionally reroutes requests from other threads to the same {@code Scheduler} thread. *

* If there is a {@link #create(FlowableOnSubscribe, BackpressureStrategy)} type source up in the - * chain, it is recommended to have {@code requestOn} false to avoid same-pool deadlock + * chain, it is recommended to have {@code requestOn} {@code false} to avoid same-pool deadlock * because requests may pile up behind an eager/blocking emitter. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
*

History: 2.1.1 - experimental * @param scheduler - * the {@link Scheduler} to perform subscription actions on - * @param requestOn if true, requests are rerouted to the given Scheduler as well (strong pipelining) - * if false, requests coming from any thread are simply forwarded to + * the {@code Scheduler} to perform subscription actions on + * @param requestOn if {@code true}, requests are rerouted to the given {@code Scheduler} as well (strong pipelining) + * if {@code false}, requests coming from any thread are simply forwarded to * the upstream on the same thread (weak pipelining) - * @return the source Publisher modified so that its subscriptions happen on the - * specified {@link Scheduler} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn * @see RxJava Threading Examples * @see #observeOn @@ -14965,20 +16275,20 @@ public final Flowable subscribeOn(@NonNull Scheduler scheduler) { @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.CUSTOM) public final Flowable subscribeOn(@NonNull Scheduler scheduler, boolean requestOn) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableSubscribeOn(this, scheduler, requestOn)); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableSubscribeOn<>(this, scheduler, requestOn)); } /** - * Returns a Flowable that emits the items emitted by the source Publisher or the items of an alternate - * Publisher if the source Publisher is empty. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} or the items of an alternate + * {@link Publisher} if the current {@code Flowable} is empty. *

- * + * *

*
Backpressure:
- *
If the source {@code Publisher} is empty, the alternate {@code Publisher} is expected to honor backpressure. - * If the source {@code Publisher} is non-empty, it is expected to honor backpressure as instead. - * In either case, if violated, a {@code MissingBackpressureException} may get + *
If the current {@code Flowable} is empty, the alternate {@code Publisher} is expected to honor backpressure. + * If the current {@code Flowable} is non-empty, it is expected to honor backpressure as instead. + * In either case, if violated, a {@link MissingBackpressureException} may get * signaled somewhere downstream. *
*
Scheduler:
@@ -14986,87 +16296,92 @@ public final Flowable subscribeOn(@NonNull Scheduler scheduler, boolean reque *
* * @param other - * the alternate Publisher to subscribe to if the source does not emit any items - * @return a Publisher that emits the items emitted by the source Publisher or the items of an - * alternate Publisher if the source Publisher is empty. + * the alternate {@code Publisher} to subscribe to if the source does not emit any items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 1.1.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable switchIfEmpty(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchIfEmpty(this, other)); + public final Flowable switchIfEmpty(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchIfEmpty<>(this, other)); } /** - * Returns a new Publisher by applying a function that you supply to each item emitted by the source - * Publisher that returns a Publisher, and then emitting the items emitted by the most recently emitted - * of these Publishers. + * Returns a new {@code Flowable} by applying a function that you supply to each item emitted by the current + * {@code Flowable} that returns a {@link Publisher}, and then emitting the items emitted by the most recently emitted + * of these {@code Publisher}s. *

- * The resulting Publisher completes if both the upstream Publisher and the last inner Publisher, if any, complete. - * If the upstream Publisher signals an onError, the inner Publisher is canceled and the error delivered in-sequence. + * The resulting {@code Flowable} completes if both the current {@code Flowable} and the last inner {@code Publisher}, if any, complete. + * If the current {@code Flowable} signals an {@code onError}, the inner {@code Publisher} is canceled and the error delivered in-sequence. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner Publishers and the output + * @param the element type of the inner {@code Publisher}s and the output * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher - * @return a Flowable that emits the items emitted by the Publisher returned from applying {@code func} to the most recently emitted item emitted by the source Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #switchMapDelayError(Function) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable switchMap(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable switchMap(@NonNull Function> mapper) { return switchMap(mapper, bufferSize()); } /** - * Returns a new Publisher by applying a function that you supply to each item emitted by the source - * Publisher that returns a Publisher, and then emitting the items emitted by the most recently emitted - * of these Publishers. + * Returns a new {@code Flowable} by applying a function that you supply to each item emitted by the current + * {@code Flowable} that returns a {@link Publisher}, and then emitting the items emitted by the most recently emitted + * of these {@code Publisher}s. *

- * The resulting Publisher completes if both the upstream Publisher and the last inner Publisher, if any, complete. - * If the upstream Publisher signals an onError, the inner Publisher is canceled and the error delivered in-sequence. + * The resulting {@code Flowable} completes if both the current {@code Flowable} and the last inner {@code Publisher}, if any, complete. + * If the current {@code Flowable} signals an {@code onError}, the inner {@code Publisher} is canceled and the error delivered in-sequence. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner Publishers and the output + * @param the element type of the inner {@code Publisher}s and the output * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param bufferSize - * the number of elements to prefetch from the current active inner Publisher - * @return a Flowable that emits the items emitted by the Publisher returned from applying {@code func} to the most recently emitted item emitted by the source Publisher + * the number of elements to prefetch from the current active inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @see #switchMapDelayError(Function, int) */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable switchMap(Function> mapper, int bufferSize) { + @NonNull + public final <@NonNull R> Flowable switchMap(@NonNull Function> mapper, int bufferSize) { return switchMap0(mapper, bufferSize, false); } @@ -15075,35 +16390,36 @@ public final Flowable switchMap(Function - * + * *

* Since a {@code CompletableSource} doesn't produce any items, the resulting reactive type of * this operator is a {@link Completable} that can only indicate successful completion or * a failure in any of the inner {@code CompletableSource}s or the failure of the current - * {@link Flowable}. + * {@code Flowable}. *

*
Backpressure:
- *
The operator consumes the current {@link Flowable} in an unbounded manner and otherwise + *
The operator consumes the current {@code Flowable} in an unbounded manner and otherwise * does not have backpressure in its return type because no items are ever produced.
*
Scheduler:
*
{@code switchMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
*
If either this {@code Flowable} or the active {@code CompletableSource} signals an {@code onError}, - * the resulting {@code Completable} is terminated immediately with that {@code Throwable}. + * the resulting {@code Completable} is terminated immediately with that {@link Throwable}. * Use the {@link #switchMapCompletableDelayError(Function)} to delay such inner failures until * every inner {@code CompletableSource}s and the main {@code Flowable} terminates in some fashion. * If they fail concurrently, the operator may combine the {@code Throwable}s into a * {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException} * and signal it to the downstream instead. If any inactivated (switched out) {@code CompletableSource} * signals an {@code onError} late, the {@code Throwable}s will be signaled to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. *
*
*

History: 2.1.11 - experimental * @param mapper the function called with each upstream item and should return a - * {@link CompletableSource} to be subscribed to and awaited for + * {@code CompletableSource} to be subscribed to and awaited for * (non blockingly) for its terminal event - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see #switchMapCompletableDelayError(Function) * @since 2.2 */ @@ -15112,8 +16428,8 @@ public final Flowable switchMap(Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchMapCompletable(this, mapper, false)); + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapCompletable<>(this, mapper, false)); } /** @@ -15127,30 +16443,31 @@ public final Completable switchMapCompletable(@NonNull Function *

Backpressure:
- *
The operator consumes the current {@link Flowable} in an unbounded manner and otherwise + *
The operator consumes the current {@code Flowable} in an unbounded manner and otherwise * does not have backpressure in its return type because no items are ever produced.
*
Scheduler:
*
{@code switchMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
Errors of this {@code Flowable} and all the {@code CompletableSource}s, who had the chance + *
The errors of this {@code Flowable} and all the {@code CompletableSource}s, who had the chance * to run to their completion, are delayed until * all of them terminate in some fashion. At this point, if there was only one failure, the respective - * {@code Throwable} is emitted to the downstream. If there was more than one failure, the + * {@link Throwable} is emitted to the downstream. If there was more than one failure, the * operator combines all {@code Throwable}s into a {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException} * and signals that to the downstream. * If any inactivated (switched out) {@code CompletableSource} * signals an {@code onError} late, the {@code Throwable}s will be signaled to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. *
*
*

History: 2.1.11 - experimental * @param mapper the function called with each upstream item and should return a - * {@link CompletableSource} to be subscribed to and awaited for + * {@code CompletableSource} to be subscribed to and awaited for * (non blockingly) for its terminal event - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see #switchMapCompletable(Function) * @since 2.2 */ @@ -15159,35 +16476,36 @@ public final Completable switchMapCompletable(@NonNull Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchMapCompletable(this, mapper, true)); + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapCompletable<>(this, mapper, true)); } /** - * Returns a new Publisher by applying a function that you supply to each item emitted by the source - * Publisher that returns a Publisher, and then emitting the items emitted by the most recently emitted - * of these Publishers and delays any error until all Publishers terminate. + * Returns a new {@code Flowable} by applying a function that you supply to each item emitted by the current + * {@code Flowable} that returns a {@link Publisher}, and then emitting the items emitted by the most recently emitted + * of these {@code Publisher}s and delays any error until all {@code Publisher}s terminate. *

- * The resulting Publisher completes if both the upstream Publisher and the last inner Publisher, if any, complete. - * If the upstream Publisher signals an onError, the termination of the last inner Publisher will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner Publishers signaled. + * The resulting {@code Flowable} completes if both the current {@code Flowable} and the last inner {@code Publisher}, if any, complete. + * If the current {@code Flowable} signals an {@code onError}, the termination of the last inner {@code Publisher} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code Publisher}s signaled. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner Publishers and the output + * @param the element type of the inner {@code Publisher}s and the output * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher - * @return a Flowable that emits the items emitted by the Publisher returned from applying {@code func} to the most recently emitted item emitted by the source Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #switchMap(Function) * @since 2.0 @@ -15195,37 +16513,40 @@ public final Completable switchMapCompletableDelayError(@NonNull Function Flowable switchMapDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Flowable switchMapDelayError(@NonNull Function> mapper) { return switchMapDelayError(mapper, bufferSize()); } /** - * Returns a new Publisher by applying a function that you supply to each item emitted by the source - * Publisher that returns a Publisher, and then emitting the items emitted by the most recently emitted - * of these Publishers and delays any error until all Publishers terminate. + * Returns a new {@code Flowable} by applying a function that you supply to each item emitted by the current + * {@code Flowable} that returns a {@link Publisher}, and then emitting the items emitted by the most recently emitted + * of these {@code Publisher}s and delays any error until all {@code Publisher}s terminate. *

- * The resulting Publisher completes if both the upstream Publisher and the last inner Publisher, if any, complete. - * If the upstream Publisher signals an onError, the termination of the last inner Publisher will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner Publishers signaled. + * The resulting {@code Flowable} completes if both the current {@code Flowable} and the last inner {@code Publisher}, if any, complete. + * If the current {@code Flowable} signals an {@code onError}, the termination of the last inner {@code Publisher} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code Publisher}s signaled. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed in an * unbounded manner (i.e., without backpressure) and the inner {@code Publisher}s are expected to honor - * backpressure but it is not enforced; the operator won't signal a {@code MissingBackpressureException} - * but the violation may lead to {@code OutOfMemoryError} due to internal buffer bloat.
+ * backpressure but it is not enforced; the operator won't signal a {@link MissingBackpressureException} + * but the violation may lead to {@link OutOfMemoryError} due to internal buffer bloat. *
Scheduler:
*
{@code switchMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner Publishers and the output + * @param the element type of the inner {@code Publisher}s and the output * @param mapper - * a function that, when applied to an item emitted by the source Publisher, returns a - * Publisher + * a function that, when applied to an item emitted by the current {@code Flowable}, returns a + * {@code Publisher} * @param bufferSize - * the number of elements to prefetch from the current active inner Publisher - * @return a Flowable that emits the items emitted by the Publisher returned from applying {@code func} to the most recently emitted item emitted by the source Publisher + * the number of elements to prefetch from the current active inner {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @see #switchMap(Function, int) * @since 2.0 @@ -15233,12 +16554,13 @@ public final Flowable switchMapDelayError(Function Flowable switchMapDelayError(Function> mapper, int bufferSize) { + @NonNull + public final <@NonNull R> Flowable switchMapDelayError(@NonNull Function> mapper, int bufferSize) { return switchMap0(mapper, bufferSize, true); } - Flowable switchMap0(Function> mapper, int bufferSize, boolean delayError) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Flowable switchMap0(Function> mapper, int bufferSize, boolean delayError) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") @@ -15248,7 +16570,7 @@ Flowable switchMap0(Function> } return FlowableScalarXMap.scalarXMap(v, mapper); } - return RxJavaPlugins.onAssembly(new FlowableSwitchMap(this, mapper, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new FlowableSwitchMap<>(this, mapper, bufferSize, delayError)); } /** @@ -15257,7 +16579,7 @@ Flowable switchMap0(Function> * available while failing immediately if this {@code Flowable} or any of the * active inner {@code MaybeSource}s fail. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -15267,8 +16589,8 @@ Flowable switchMap0(Function> *
Error handling:
*
This operator terminates with an {@code onError} if this {@code Flowable} or any of * the inner {@code MaybeSource}s fail while they are active. When this happens concurrently, their - * individual {@code Throwable} errors may get combined and emitted as a single - * {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException}. Otherwise, a late + * individual {@link Throwable} errors may get combined and emitted as a single + * {@link CompositeException}. Otherwise, a late * (i.e., inactive or switched out) {@code onError} from this {@code Flowable} or from any of * the inner {@code MaybeSource}s will be forwarded to the global error handler via * {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable)} as @@ -15279,7 +16601,8 @@ Flowable switchMap0(Function> * @param mapper the function called with the current upstream event and should * return a {@code MaybeSource} to replace the current active inner source * and get subscribed to. - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see #switchMapMaybeDelayError(Function) * @since 2.2 */ @@ -15287,9 +16610,9 @@ Flowable switchMap0(Function> @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable switchMapMaybe(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchMapMaybe(this, mapper, false)); + public final <@NonNull R> Flowable switchMapMaybe(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapMaybe<>(this, mapper, false)); } /** @@ -15297,7 +16620,7 @@ public final Flowable switchMapMaybe(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -15310,7 +16633,8 @@ public final Flowable switchMapMaybe(@NonNull Function Flowable switchMapMaybe(@NonNull Function Flowable switchMapMaybeDelayError(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchMapMaybe(this, mapper, true)); + public final <@NonNull R> Flowable switchMapMaybeDelayError(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapMaybe<>(this, mapper, true)); } /** @@ -15329,7 +16653,7 @@ public final Flowable switchMapMaybeDelayError(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -15339,8 +16663,8 @@ public final Flowable switchMapMaybeDelayError(@NonNull FunctionError handling: *
This operator terminates with an {@code onError} if this {@code Flowable} or any of * the inner {@code SingleSource}s fail while they are active. When this happens concurrently, their - * individual {@code Throwable} errors may get combined and emitted as a single - * {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException}. Otherwise, a late + * individual {@link Throwable} errors may get combined and emitted as a single + * {@link CompositeException}. Otherwise, a late * (i.e., inactive or switched out) {@code onError} from this {@code Flowable} or from any of * the inner {@code SingleSource}s will be forwarded to the global error handler via * {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable)} as @@ -15351,7 +16675,8 @@ public final Flowable switchMapMaybeDelayError(@NonNull Function Flowable switchMapMaybeDelayError(@NonNull Function Flowable switchMapSingle(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchMapSingle(this, mapper, false)); + public final <@NonNull R> Flowable switchMapSingle(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapSingle<>(this, mapper, false)); } /** @@ -15369,7 +16694,7 @@ public final Flowable switchMapSingle(@NonNull Function - * + * *
*
Backpressure:
*
The operator honors backpressure from downstream. The main {@code Flowable} is consumed in an @@ -15382,7 +16707,8 @@ public final Flowable switchMapSingle(@NonNull Function Flowable switchMapSingle(@NonNull Function Flowable switchMapSingleDelayError(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new FlowableSwitchMapSingle(this, mapper, true)); + public final <@NonNull R> Flowable switchMapSingleDelayError(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapSingle<>(this, mapper, true)); } /** - * Returns a Flowable that emits only the first {@code count} items emitted by the source Publisher. If the source emits fewer than - * {@code count} items then all of its items are emitted. + * Returns a {@code Flowable} that emits only the first {@code count} items emitted by the current {@code Flowable}. + * If the source emits fewer than {@code count} items then all of its items are emitted. *

- * + * *

- * This method returns a Publisher that will invoke a subscribing {@link Subscriber}'s + * This method returns a {@code Flowable} that will invoke a subscribing {@link Subscriber}'s * {@link Subscriber#onNext onNext} function a maximum of {@code count} times before invoking * {@link Subscriber#onComplete onComplete}. *

@@ -15410,19 +16736,19 @@ public final Flowable switchMapSingleDelayError(@NonNull Function * The operator requests at most the given {@code count} of items from upstream even - * if the downstream requests more than that. For example, given a {@code limit(5)}, + * if the downstream requests more than that. For example, given a {@code take(5)}, * if the downstream requests 1, a request of 1 is submitted to the upstream * and the operator remembers that only 4 items can be requested now on. A request * of 5 at this point will request 4 from the upstream and any subsequent requests will * be ignored. *

- * Note that requests are negotiated on an operator boundary and {@code limit}'s amount + * Note that requests are negotiated on an operator boundary and {@code take}'s amount * may not be preserved further upstream. For example, - * {@code source.observeOn(Schedulers.computation()).limit(5)} will still request the + * {@code source.observeOn(Schedulers.computation()).take(5)} will still request the * default (128) elements from the given {@code source}. *

*
Backpressure:
- *
The source {@code Publisher} is consumed in a bounded manner.
+ *
The current {@code Flowable} is consumed in a bounded manner.
*
Scheduler:
*
This version of {@code take} does not operate by default on a particular {@link Scheduler}.
*
@@ -15431,64 +16757,67 @@ public final Flowable switchMapSingleDelayError(@NonNull FunctionReactiveX operators documentation: Take */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable take(long count) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); } - return RxJavaPlugins.onAssembly(new FlowableTake(this, count)); + return RxJavaPlugins.onAssembly(new FlowableTake<>(this, count)); } /** - * Returns a Flowable that emits those items emitted by source Publisher before a specified time runs + * Returns a {@code Flowable} that emits those items emitted by source {@link Publisher} before a specified time runs * out. *

* If time runs out before the {@code Flowable} completes normally, the {@code onComplete} event will be * signaled on the default {@code computation} {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
This version of {@code take} operates by default on the {@code computation} {@link Scheduler}.
+ *
This version of {@code take} operates by default on the {@code computation} {@code Scheduler}.
*
* * @param time * the length of the time window * @param unit * the time unit of {@code time} - * @return a Flowable that emits those items emitted by the source Publisher before the time runs out + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Take */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable take(long time, TimeUnit unit) { + @NonNull + public final Flowable take(long time, @NonNull TimeUnit unit) { return takeUntil(timer(time, unit)); } /** - * Returns a Flowable that emits those items emitted by source Publisher before a specified time (on a - * specified Scheduler) runs out. + * Returns a {@code Flowable} that emits those items emitted by source {@link Publisher} before a specified time (on a + * specified {@link Scheduler}) runs out. *

* If time runs out before the {@code Flowable} completes normally, the {@code onComplete} event will be - * signaled on the provided {@link Scheduler}. + * signaled on the provided {@code Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -15496,23 +16825,24 @@ public final Flowable take(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler used for time source - * @return a Flowable that emits those items emitted by the source Publisher before the time runs out, - * according to the specified Scheduler + * the {@code Scheduler} used for time source + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Take */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable take(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable take(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return takeUntil(timer(time, unit, scheduler)); } /** - * Returns a Flowable that emits at most the last {@code count} items emitted by the source Publisher. If the source emits fewer than + * Returns a {@code Flowable} that emits at most the last {@code count} items emitted by the current {@code Flowable}. If the source emits fewer than * {@code count} items then all of its items are emitted. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream if the {@code count} is non-zero; ignores @@ -15522,37 +16852,38 @@ public final Flowable take(long time, TimeUnit unit, Scheduler scheduler) { *
* * @param count - * the maximum number of items to emit from the end of the sequence of items emitted by the source - * Publisher - * @return a Flowable that emits at most the last {@code count} items emitted by the source Publisher - * @throws IndexOutOfBoundsException - * if {@code count} is less than zero + * the maximum number of items to emit from the end of the sequence of items emitted by the current + * {@code Flowable} + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException + * if {@code count} is negative * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable takeLast(int count) { if (count < 0) { - throw new IndexOutOfBoundsException("count >= 0 required but it was " + count); + throw new IllegalArgumentException("count >= 0 required but it was " + count); } else if (count == 0) { - return RxJavaPlugins.onAssembly(new FlowableIgnoreElements(this)); + return RxJavaPlugins.onAssembly(new FlowableIgnoreElements<>(this)); } else if (count == 1) { - return RxJavaPlugins.onAssembly(new FlowableTakeLastOne(this)); + return RxJavaPlugins.onAssembly(new FlowableTakeLastOne<>(this)); } - return RxJavaPlugins.onAssembly(new FlowableTakeLast(this, count)); + return RxJavaPlugins.onAssembly(new FlowableTakeLast<>(this, count)); } /** - * Returns a Flowable that emits at most a specified number of items from the source Publisher that were - * emitted in a specified window of time before the Publisher completed. + * Returns a {@code Flowable} that emits at most a specified number of items from the current {@code Flowable} that were + * emitted in a specified window of time before the current {@code Flowable} completed. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it).
*
Scheduler:
*
{@code takeLast} does not operate on any particular scheduler but uses the current time @@ -15565,29 +16896,31 @@ public final Flowable takeLast(int count) { * the length of the time window * @param unit * the time unit of {@code time} - * @return a Flowable that emits at most {@code count} items from the source Publisher that were emitted - * in a specified window of time before the Publisher completed + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is negative * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable takeLast(long count, long time, TimeUnit unit) { + @NonNull + public final Flowable takeLast(long count, long time, @NonNull TimeUnit unit) { return takeLast(count, time, unit, Schedulers.computation(), false, bufferSize()); } /** - * Returns a Flowable that emits at most a specified number of items from the source Publisher that were - * emitted in a specified window of time before the Publisher completed, where the timing information is - * provided by a given Scheduler. + * Returns a {@code Flowable} that emits at most a specified number of items from the current {@code Flowable} that were + * emitted in a specified window of time before the current {@code Flowable} completed, where the timing information is + * provided by a given {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it).
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use for tracking the current time
+ *
You specify which {@code Scheduler} this operator will use for tracking the current time
*
* * @param count @@ -15597,33 +16930,33 @@ public final Flowable takeLast(long count, long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the {@link Scheduler} that provides the timestamps for the observed items - * @return a Flowable that emits at most {@code count} items from the source Publisher that were emitted - * in a specified window of time before the Publisher completed, where the timing information is - * provided by the given {@code scheduler} - * @throws IndexOutOfBoundsException + * the {@code Scheduler} that provides the timestamps for the observed items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException * if {@code count} is less than zero * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable takeLast(long count, long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable takeLast(long count, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return takeLast(count, time, unit, scheduler, false, bufferSize()); } /** - * Returns a Flowable that emits at most a specified number of items from the source Publisher that were - * emitted in a specified window of time before the Publisher completed, where the timing information is - * provided by a given Scheduler. + * Returns a {@code Flowable} that emits at most a specified number of items from the current {@code Flowable} that were + * emitted in a specified window of time before the current {@code Flowable} completed, where the timing information is + * provided by a given {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it).
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use for tracking the current time
+ *
You specify which {@code Scheduler} this operator will use for tracking the current time
*
* * @param count @@ -15633,43 +16966,42 @@ public final Flowable takeLast(long count, long time, TimeUnit unit, Schedule * @param unit * the time unit of {@code time} * @param scheduler - * the {@link Scheduler} that provides the timestamps for the observed items + * the {@code Scheduler} that provides the timestamps for the observed items * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped * @param bufferSize * the hint about how many elements to expect to be last - * @return a Flowable that emits at most {@code count} items from the source Publisher that were emitted - * in a specified window of time before the Publisher completed, where the timing information is - * provided by the given {@code scheduler} - * @throws IndexOutOfBoundsException - * if {@code count} is less than zero + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is negative or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable takeLast(long count, long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public final Flowable takeLast(long count, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError, int bufferSize) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (count < 0) { - throw new IndexOutOfBoundsException("count >= 0 required but it was " + count); + throw new IllegalArgumentException("count >= 0 required but it was " + count); } - return RxJavaPlugins.onAssembly(new FlowableTakeLastTimed(this, count, time, unit, scheduler, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new FlowableTakeLastTimed<>(this, count, time, unit, scheduler, bufferSize, delayError)); } /** - * Returns a Flowable that emits the items from the source Publisher that were emitted in a specified - * window of time before the Publisher completed. + * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified + * window of time before the current {@code Flowable} completed. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it) but note that this may - * lead to {@code OutOfMemoryError} due to internal buffer bloat. + * lead to {@link OutOfMemoryError} due to internal buffer bloat. * Consider using {@link #takeLast(long, long, TimeUnit)} in this case.
*
Scheduler:
*
This version of {@code takeLast} operates by default on the {@code computation} {@link Scheduler}.
@@ -15679,27 +17011,28 @@ public final Flowable takeLast(long count, long time, TimeUnit unit, Schedule * the length of the time window * @param unit * the time unit of {@code time} - * @return a Flowable that emits the items from the source Publisher that were emitted in the window of - * time before the Publisher completed specified by {@code time} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable takeLast(long time, TimeUnit unit) { + @NonNull + public final Flowable takeLast(long time, @NonNull TimeUnit unit) { return takeLast(time, unit, Schedulers.computation(), false, bufferSize()); } /** - * Returns a Flowable that emits the items from the source Publisher that were emitted in a specified - * window of time before the Publisher completed. + * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified + * window of time before the current {@code Flowable} completed. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it) but note that this may - * lead to {@code OutOfMemoryError} due to internal buffer bloat. + * lead to {@link OutOfMemoryError} due to internal buffer bloat. * Consider using {@link #takeLast(long, long, TimeUnit)} in this case.
*
Scheduler:
*
This version of {@code takeLast} operates by default on the {@code computation} {@link Scheduler}.
@@ -15710,33 +17043,34 @@ public final Flowable takeLast(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped - * @return a Flowable that emits the items from the source Publisher that were emitted in the window of - * time before the Publisher completed specified by {@code time} + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable takeLast(long time, TimeUnit unit, boolean delayError) { + @NonNull + public final Flowable takeLast(long time, @NonNull TimeUnit unit, boolean delayError) { return takeLast(time, unit, Schedulers.computation(), delayError, bufferSize()); } /** - * Returns a Flowable that emits the items from the source Publisher that were emitted in a specified - * window of time before the Publisher completed, where the timing information is provided by a specified - * Scheduler. + * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified + * window of time before the current {@code Flowable} completed, where the timing information is provided by a specified + * {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it) but note that this may - * lead to {@code OutOfMemoryError} due to internal buffer bloat. + * lead to {@link OutOfMemoryError} due to internal buffer bloat. * Consider using {@link #takeLast(long, long, TimeUnit, Scheduler)} in this case.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -15744,33 +17078,33 @@ public final Flowable takeLast(long time, TimeUnit unit, boolean delayError) * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that provides the timestamps for the Observed items - * @return a Flowable that emits the items from the source Publisher that were emitted in the window of - * time before the Publisher completed specified by {@code time}, where the timing information is - * provided by {@code scheduler} + * the {@code Scheduler} that provides the timestamps for the observed items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable takeLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return takeLast(time, unit, scheduler, false, bufferSize()); } /** - * Returns a Flowable that emits the items from the source Publisher that were emitted in a specified - * window of time before the Publisher completed, where the timing information is provided by a specified - * Scheduler. + * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified + * window of time before the current {@code Flowable} completed, where the timing information is provided by a specified + * {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it) but note that this may - * lead to {@code OutOfMemoryError} due to internal buffer bloat. + * lead to {@link OutOfMemoryError} due to internal buffer bloat. * Consider using {@link #takeLast(long, long, TimeUnit, Scheduler)} in this case.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -15778,36 +17112,36 @@ public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler) * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that provides the timestamps for the Observed items + * the {@code Scheduler} that provides the timestamps for the observed items * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped - * @return a Flowable that emits the items from the source Publisher that were emitted in the window of - * time before the Publisher completed specified by {@code time}, where the timing information is - * provided by {@code scheduler} + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError) { + @NonNull + public final Flowable takeLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { return takeLast(time, unit, scheduler, delayError, bufferSize()); } /** - * Returns a Flowable that emits the items from the source Publisher that were emitted in a specified - * window of time before the Publisher completed, where the timing information is provided by a specified - * Scheduler. + * Returns a {@code Flowable} that emits the items from the current {@code Flowable} that were emitted in a specified + * window of time before the current {@code Flowable} completed, where the timing information is provided by a specified + * {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., no backpressure is applied to it) but note that this may - * lead to {@code OutOfMemoryError} due to internal buffer bloat. + * lead to {@link OutOfMemoryError} due to internal buffer bloat. * Consider using {@link #takeLast(long, long, TimeUnit, Scheduler)} in this case.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -15815,29 +17149,30 @@ public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler, * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that provides the timestamps for the Observed items + * the {@code Scheduler} that provides the timestamps for the observed items * @param delayError - * if true, an exception signaled by the current Flowable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signaled and all regular elements dropped + * if {@code true}, an exception signaled by the current {@code Flowable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped * @param bufferSize * the hint about how many elements to expect to be last - * @return a Flowable that emits the items from the source Publisher that were emitted in the window of - * time before the Publisher completed specified by {@code time}, where the timing information is - * provided by {@code scheduler} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize) { + @NonNull + public final Flowable takeLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError, int bufferSize) { return takeLast(Long.MAX_VALUE, time, unit, scheduler, delayError, bufferSize); } /** - * Returns a Flowable that emits items emitted by the source Publisher, checks the specified predicate + * Returns a {@code Flowable} that emits items emitted by the current {@code Flowable}, checks the specified predicate * for each item, and then completes when the condition is satisfied. *

- * + * *

* The difference between this operator and {@link #takeWhile(Predicate)} is that here, the condition is * evaluated after the item is emitted. @@ -15851,9 +17186,9 @@ public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler, *

* * @param stopPredicate - * a function that evaluates an item emitted by the source Publisher and returns a Boolean - * @return a Flowable that first emits items emitted by the source Publisher, checks the specified - * condition after each item, and then completes when the condition is satisfied. + * a function that evaluates an item emitted by the current {@code Flowable} and returns a {@link Boolean} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code stopPredicate} is {@code null} * @see ReactiveX operators documentation: TakeUntil * @see Flowable#takeWhile(Predicate) * @since 1.1.0 @@ -15862,58 +17197,59 @@ public final Flowable takeLast(long time, TimeUnit unit, Scheduler scheduler, @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable takeUntil(Predicate stopPredicate) { - ObjectHelper.requireNonNull(stopPredicate, "stopPredicate is null"); - return RxJavaPlugins.onAssembly(new FlowableTakeUntilPredicate(this, stopPredicate)); + public final Flowable takeUntil(@NonNull Predicate stopPredicate) { + Objects.requireNonNull(stopPredicate, "stopPredicate is null"); + return RxJavaPlugins.onAssembly(new FlowableTakeUntilPredicate<>(this, stopPredicate)); } /** - * Returns a Flowable that emits the items emitted by the source Publisher until a second Publisher - * emits an item. + * Returns a {@code Flowable} that emits the items emitted by the current {@code Flowable} until a second {@link Publisher} + * emits an item or completes. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code takeUntil} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * the Publisher whose first emitted item will cause {@code takeUntil} to stop emitting items - * from the source Publisher + * the {@code Publisher} whose first emitted item or completion will cause {@code takeUntil} to stop emitting items + * from the current {@code Flowable} * @param * the type of items emitted by {@code other} - * @return a Flowable that emits the items emitted by the source Publisher until such time as {@code other} emits its first item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable takeUntil(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new FlowableTakeUntil(this, other)); + public final <@NonNull U> Flowable takeUntil(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new FlowableTakeUntil<>(this, other)); } /** - * Returns a Flowable that emits items emitted by the source Publisher so long as each item satisfied a + * Returns a {@code Flowable} that emits items emitted by the current {@code Flowable} so long as each item satisfied a * specified condition, and then completes as soon as this condition is not satisfied. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code takeWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates an item emitted by the source Publisher and returns a Boolean - * @return a Flowable that emits the items from the source Publisher so long as each item satisfies the - * condition defined by {@code predicate}, then completes + * a function that evaluates an item emitted by the current {@code Flowable} and returns a {@link Boolean} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: TakeWhile * @see Flowable#takeUntil(Predicate) */ @@ -15921,19 +17257,19 @@ public final Flowable takeUntil(Publisher other) { @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable takeWhile(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new FlowableTakeWhile(this, predicate)); + public final Flowable takeWhile(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new FlowableTakeWhile<>(this, predicate)); } /** - * Returns a Flowable that emits only the first item emitted by the source Publisher during sequential + * Returns a {@code Flowable} that emits only the first item emitted by the current {@code Flowable} during sequential * time windows of a specified duration. *

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

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -15945,30 +17281,69 @@ public final Flowable takeWhile(Predicate predicate) { * time to wait before emitting another item after emitting the last item * @param unit * the unit of time of {@code windowDuration} - * @return a Flowable that performs the throttle operation + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable throttleFirst(long windowDuration, TimeUnit unit) { + @NonNull + public final Flowable throttleFirst(long windowDuration, @NonNull TimeUnit unit) { return throttleFirst(windowDuration, unit, Schedulers.computation()); } /** - * Returns a Flowable that emits only the first item emitted by the source Publisher during sequential - * time windows of a specified duration, where the windows are managed by a specified Scheduler. + * Returns a {@code Flowable} that emits only the first item emitted by the current {@code Flowable} during sequential + * time windows of a specified duration, where the windows are managed by a specified {@link Scheduler}. *

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

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param skipDuration + * time to wait before emitting another item after emitting the last item + * @param unit + * the unit of time of {@code skipDuration} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each + * event + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @see ReactiveX operators documentation: Sample + * @see RxJava wiki: Backpressure + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.ERROR) + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Flowable throttleFirst(long skipDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableThrottleFirstTimed<>(this, skipDuration, unit, scheduler, null)); + } + + /** + * Returns a {@code Flowable} that emits only the first item emitted by the current {@code Flowable} during sequential + * time windows of a specified duration, where the windows are managed by a specified {@link Scheduler}. + *

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

+ * + *

+ *
Backpressure:
+ *
This operator does not support backpressure as it uses time to control data flow.
+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param skipDuration @@ -15976,30 +17351,37 @@ public final Flowable throttleFirst(long windowDuration, TimeUnit unit) { * @param unit * the unit of time of {@code skipDuration} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each * event - * @return a Flowable that performs the throttle operation + * @param onDropped + * called when an item doesn't get delivered to the downstream + * + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} or {@code onDropped} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure + * @since 3.1.6 - Experimental */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableThrottleFirstTimed(this, skipDuration, unit, scheduler)); + @Experimental + public final Flowable throttleFirst(long skipDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new FlowableThrottleFirstTimed<>(this, skipDuration, unit, scheduler, onDropped)); } /** - * Returns a Flowable that emits only the last item emitted by the source Publisher during sequential + * Returns a {@code Flowable} that emits only the last item emitted by the current {@code Flowable} during sequential * time windows of a specified duration. *

* This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas * {@link #throttleFirst} does not tick, it just tracks the passage of time. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -16008,11 +17390,12 @@ public final Flowable throttleFirst(long skipDuration, TimeUnit unit, Schedul *
* * @param intervalDuration - * duration of windows within which the last item emitted by the source Publisher will be + * duration of windows within which the last item emitted by the current {@code Flowable} will be * emitted * @param unit * the unit of time of {@code intervalDuration} - * @return a Flowable that performs the throttle operation + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @see #sample(long, TimeUnit) @@ -16020,34 +17403,36 @@ public final Flowable throttleFirst(long skipDuration, TimeUnit unit, Schedul @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable throttleLast(long intervalDuration, TimeUnit unit) { + @NonNull + public final Flowable throttleLast(long intervalDuration, @NonNull TimeUnit unit) { return sample(intervalDuration, unit); } /** - * Returns a Flowable that emits only the last item emitted by the source Publisher during sequential - * time windows of a specified duration, where the duration is governed by a specified Scheduler. + * Returns a {@code Flowable} that emits only the last item emitted by the current {@code Flowable} during sequential + * time windows of a specified duration, where the duration is governed by a specified {@link Scheduler}. *

- * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas - * {@link #throttleFirst} does not tick, it just tracks the passage of time. + * This differs from {@link #throttleFirst(long, TimeUnit, Scheduler)} in that this ticks along at a scheduled interval whereas + * {@code throttleFirst} does not tick, it just tracks the passage of time. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param intervalDuration - * duration of windows within which the last item emitted by the source Publisher will be + * duration of windows within which the last item emitted by the current {@code Flowable} will be * emitted * @param unit * the unit of time of {@code intervalDuration} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each * event - * @return a Flowable that performs the throttle operation + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Sample * @see RxJava wiki: Backpressure * @see #sample(long, TimeUnit, Scheduler) @@ -16055,16 +17440,58 @@ public final Flowable throttleLast(long intervalDuration, TimeUnit unit) { @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable throttleLast(long intervalDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return sample(intervalDuration, unit, scheduler); } + /** + * Returns a {@code Flowable} that emits only the last item emitted by the current {@code Flowable} during sequential + * time windows of a specified duration, where the duration is governed by a specified {@link Scheduler}. + *

+ * This differs from {@link #throttleFirst(long, TimeUnit, Scheduler)} in that this ticks along at a scheduled interval whereas + * {@code throttleFirst} does not tick, it just tracks the passage of time. + *

+ * + *

+ *
Backpressure:
+ *
This operator does not support backpressure as it uses time to control data flow.
+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param intervalDuration + * duration of windows within which the last item emitted by the current {@code Flowable} will be + * emitted + * @param unit + * the unit of time of {@code intervalDuration} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each + * event + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Sample + * @see RxJava wiki: Backpressure + * @see #sample(long, TimeUnit, Scheduler) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.ERROR) + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Flowable throttleLast(long intervalDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + return sample(intervalDuration, unit, scheduler, false, onDropped); + } + /** * Throttles items from the upstream {@code Flowable} by first emitting the next * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* Unlike the option with {@link #throttleLatest(long, TimeUnit, boolean)}, the very last item being held back * (if any) is not emitted when the upstream completes. @@ -16084,7 +17511,8 @@ public final Flowable throttleLast(long intervalDuration, TimeUnit unit, Sche * @param timeout the time to wait after an item emission towards the downstream * before trying to emit the latest item from upstream again * @param unit the time unit - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.2 * @see #throttleLatest(long, TimeUnit, boolean) * @see #throttleLatest(long, TimeUnit, Scheduler) @@ -16092,7 +17520,8 @@ public final Flowable throttleLast(long intervalDuration, TimeUnit unit, Sche @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable throttleLatest(long timeout, TimeUnit unit) { + @NonNull + public final Flowable throttleLatest(long timeout, @NonNull TimeUnit unit) { return throttleLatest(timeout, unit, Schedulers.computation(), false); } @@ -16101,7 +17530,7 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit) { * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* If no items were emitted from the upstream during this timeout phase, the next * upstream item is emitted immediately and the timeout window starts from then. @@ -16122,14 +17551,16 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit) { * immediately when the upstream completes, regardless if there is * a timeout window active or not. If {@code false}, the very last * upstream item is ignored and the flow terminates. - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see #throttleLatest(long, TimeUnit, Scheduler, boolean) * @since 2.2 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable throttleLatest(long timeout, TimeUnit unit, boolean emitLast) { + @NonNull + public final Flowable throttleLatest(long timeout, @NonNull TimeUnit unit, boolean emitLast) { return throttleLatest(timeout, unit, Schedulers.computation(), emitLast); } @@ -16138,7 +17569,7 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit, boolean emi * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* Unlike the option with {@link #throttleLatest(long, TimeUnit, Scheduler, boolean)}, the very last item being held back * (if any) is not emitted when the upstream completes. @@ -16158,16 +17589,18 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit, boolean emi * @param timeout the time to wait after an item emission towards the downstream * before trying to emit the latest item from upstream again * @param unit the time unit - * @param scheduler the {@link Scheduler} where the timed wait and latest item + * @param scheduler the {@code Scheduler} where the timed wait and latest item * emission will be performed - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see #throttleLatest(long, TimeUnit, Scheduler, boolean) * @since 2.2 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable throttleLatest(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable throttleLatest(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return throttleLatest(timeout, unit, scheduler, false); } @@ -16176,7 +17609,7 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit, Scheduler s * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* If no items were emitted from the upstream during this timeout phase, the next * upstream item is emitted immediately and the timeout window starts from then. @@ -16193,34 +17626,89 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit, Scheduler s * @param timeout the time to wait after an item emission towards the downstream * before trying to emit the latest item from upstream again * @param unit the time unit - * @param scheduler the {@link Scheduler} where the timed wait and latest item + * @param scheduler the {@code Scheduler} where the timed wait and latest item * emission will be performed * @param emitLast If {@code true}, the very last item from the upstream will be emitted * immediately when the upstream completes, regardless if there is * a timeout window active or not. If {@code false}, the very last * upstream item is ignored and the flow terminates. - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable throttleLatest(long timeout, TimeUnit unit, Scheduler scheduler, boolean emitLast) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableThrottleLatest(this, timeout, unit, scheduler, emitLast)); + public final Flowable throttleLatest(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableThrottleLatest<>(this, timeout, unit, scheduler, emitLast, null)); + } + + /** + * Throttles items from the upstream {@code Flowable} by first emitting the next + * item from upstream, then periodically emitting the latest item (if any) when + * the specified timeout elapses between them, invoking the consumer for any dropped item. + *

+ * + *

+ * If no items were emitted from the upstream during this timeout phase, the next + * upstream item is emitted immediately and the timeout window starts from then. + *

+ *
Backpressure:
+ *
This operator does not support backpressure as it uses time to control data flow. + * If the downstream is not ready to receive items, a + * {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException MissingBackpressureException} + * will be signaled.
+ *
Scheduler:
+ *
You specify which {@link Scheduler} this operator will use.
+ *
Error handling:
+ *
+ * If the upstream signals an {@code onError} or {@code onDropped} callback crashes, + * the error is delivered immediately to the downstream. If both happen, a {@link CompositeException} + * is created, containing both the upstream and the callback error. + * If the {@code onDropped} callback crashes during cancellation, the exception is forwarded + * to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
+ *
+ * @param timeout the time to wait after an item emission towards the downstream + * before trying to emit the latest item from upstream again + * @param unit the time unit + * @param scheduler the {@code Scheduler} where the timed wait and latest item + * emission will be performed + * @param emitLast If {@code true}, the very last item from the upstream will be emitted + * immediately when the upstream completes, regardless if there is + * a timeout window active or not. If {@code false}, the very last + * upstream item is ignored and the flow terminates. + * @param onDropped called when an item is replaced by a newer item that doesn't get delivered + * to the downstream, including the very last item if {@code emitLast} is {@code false} + * and the current undelivered item when the sequence gets canceled. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code onDropped} is {@code null} + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.ERROR) + @SchedulerSupport(SchedulerSupport.CUSTOM) + @Experimental + public final Flowable throttleLatest(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new FlowableThrottleLatest<>(this, timeout, unit, scheduler, emitLast, onDropped)); } /** - * Returns a Flowable that mirrors the source Publisher, except that it drops items emitted by the - * source Publisher that are followed by newer items before a timeout value expires. The timer resets on + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by newer items before a timeout value expires. The timer resets on * each emission (alias to {@link #debounce(long, TimeUnit)}). *

- * Note: If items keep being emitted by the source Publisher faster than the timeout then no items - * will be emitted by the resulting Publisher. + * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items + * will be emitted by the resulting {@code Flowable}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
@@ -16229,13 +17717,13 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit, Scheduler s *
* * @param timeout - * the length of the window of time that must pass after the emission of an item from the source - * Publisher in which that Publisher emits no items in order for the item to be emitted by the - * resulting Publisher + * the length of the window of time that must pass after the emission of an item from the current + * {@code Flowable} in which it emits no items in order for the item to be emitted by the + * resulting {@code Flowable} * @param unit * the unit of time for the specified {@code timeout} - * @return a Flowable that filters out items from the source Publisher that are too quickly followed by - * newer items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Debounce * @see RxJava wiki: Backpressure * @see #debounce(long, TimeUnit) @@ -16243,37 +17731,38 @@ public final Flowable throttleLatest(long timeout, TimeUnit unit, Scheduler s @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable throttleWithTimeout(long timeout, TimeUnit unit) { + @NonNull + public final Flowable throttleWithTimeout(long timeout, @NonNull TimeUnit unit) { return debounce(timeout, unit); } /** - * Returns a Flowable that mirrors the source Publisher, except that it drops items emitted by the - * source Publisher that are followed by newer items before a timeout value expires on a specified - * Scheduler. The timer resets on each emission (alias to {@link #debounce(long, TimeUnit, Scheduler)}). + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission (alias to {@link #debounce(long, TimeUnit, Scheduler)}). *

- * Note: If items keep being emitted by the source Publisher faster than the timeout then no items - * will be emitted by the resulting Publisher. + * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items + * will be emitted by the resulting {@code Flowable}. *

- * + * *

*
Backpressure:
*
This operator does not support backpressure as it uses time to control data flow.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout - * the length of the window of time that must pass after the emission of an item from the source - * Publisher in which that Publisher emits no items in order for the item to be emitted by the - * resulting Publisher + * the length of the window of time that must pass after the emission of an item from the current + * {@code Flowable} in which it emits no items in order for the item to be emitted by the + * resulting {@code Flowable} * @param unit * the unit of time for the specified {@code timeout} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle the timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each * item - * @return a Flowable that filters out items from the source Publisher that are too quickly followed by - * newer items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Debounce * @see RxJava wiki: Backpressure * @see #debounce(long, TimeUnit, Scheduler) @@ -16281,68 +17770,115 @@ public final Flowable throttleWithTimeout(long timeout, TimeUnit unit) { @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable throttleWithTimeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return debounce(timeout, unit, scheduler); } /** - * Returns a Flowable that emits records of the time interval between consecutive items emitted by the - * source Publisher. + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, except that it drops items emitted by the + * current {@code Flowable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission (alias to {@link #debounce(long, TimeUnit, Scheduler, Consumer)}). + *

+ * Note: If items keep being emitted by the current {@code Flowable} faster than the timeout then no items + * will be emitted by the resulting {@code Flowable}. + *

+ * + *

+ *
Backpressure:
+ *
This operator does not support backpressure as it uses time to control data flow.
+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param timeout + * the length of the window of time that must pass after the emission of an item from the current + * {@code Flowable} in which it emits no items in order for the item to be emitted by the + * resulting {@code Flowable} + * @param unit + * the unit of time for the specified {@code timeout} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each + * item + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Debounce + * @see RxJava wiki: Backpressure + * @see #debounce(long, TimeUnit, Scheduler, Consumer) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.ERROR) + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Flowable throttleWithTimeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + return debounce(timeout, unit, scheduler, onDropped); + } + + /** + * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the + * current {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code timeInterval} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
*
* - * @return a Flowable that emits time interval information items + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> timeInterval() { return timeInterval(TimeUnit.MILLISECONDS, Schedulers.computation()); } /** - * Returns a Flowable that emits records of the time interval between consecutive items emitted by the - * source Publisher, where this interval is computed on a specified Scheduler. + * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the + * current {@code Flowable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
The operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ *
{@code timeInterval} does not operate on any particular scheduler but uses the current time + * from the specified {@code Scheduler}.
*
* * @param scheduler - * the {@link Scheduler} used to compute time intervals - * @return a Flowable that emits time interval information items + * the {@code Scheduler} used to compute time intervals + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Flowable> timeInterval(Scheduler scheduler) { + @NonNull + public final Flowable> timeInterval(@NonNull Scheduler scheduler) { return timeInterval(TimeUnit.MILLISECONDS, scheduler); } /** - * Returns a Flowable that emits records of the time interval between consecutive items emitted by the - * source Publisher. + * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the + * current {@code Flowable}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code timeInterval} does not operate on any particular scheduler but uses the current time @@ -16350,60 +17886,64 @@ public final Flowable> timeInterval(Scheduler scheduler) { *
* * @param unit the time unit for the current time - * @return a Flowable that emits time interval information items + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> timeInterval(TimeUnit unit) { + @NonNull + public final Flowable> timeInterval(@NonNull TimeUnit unit) { return timeInterval(unit, Schedulers.computation()); } /** - * Returns a Flowable that emits records of the time interval between consecutive items emitted by the - * source Publisher, where this interval is computed on a specified Scheduler. + * Returns a {@code Flowable} that emits records of the time interval between consecutive items emitted by the + * current {@code Flowable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
The operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ *
{@code timeInterval} does not operate on any particular scheduler but uses the current time + * from the specified {@code Scheduler}.
*
* * @param unit the time unit for the current time * @param scheduler - * the {@link Scheduler} used to compute time intervals - * @return a Flowable that emits time interval information items + * the {@code Scheduler} used to compute time intervals + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Flowable> timeInterval(TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableTimeInterval(this, unit, scheduler)); + @NonNull + public final Flowable> timeInterval(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableTimeInterval<>(this, unit, scheduler)); } /** - * Returns a Flowable that mirrors the source Publisher, but notifies Subscribers of a - * {@code TimeoutException} if an item emitted by the source Publisher doesn't arrive within a window of - * time after the emission of the previous item, where that period of time is measured by a Publisher that + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, but notifies {@link Subscriber}s of a + * {@link TimeoutException} if an item emitted by the current {@code Flowable} doesn't arrive within a window of + * time after the emission of the previous item, where that period of time is measured by a {@link Publisher} that * is a function of the previous item. *

- * + * *

* Note: The arrival of the first source item is never timed out. *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. - * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * If any of the current {@code Flowable}s violate this, it may throw an + * {@link IllegalStateException} when the current {@code Flowable} completes.
*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code immediate} {@link Scheduler}.
*
@@ -16411,35 +17951,35 @@ public final Flowable> timeInterval(TimeUnit unit, Scheduler scheduler) * @param * the timeout value type (ignored) * @param itemTimeoutIndicator - * a function that returns a Publisher for each item emitted by the source - * Publisher and that determines the timeout window for the subsequent item - * @return a Flowable that mirrors the source Publisher, but notifies Subscribers of a - * {@code TimeoutException} if an item emitted by the source Publisher takes longer to arrive than - * the time window defined by the selector for the previously emitted item + * a function that returns a {@code Publisher} for each item emitted by the current + * {@code Flowable} and that determines the timeout window for the subsequent item + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code itemTimeoutIndicator} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable timeout(Function> itemTimeoutIndicator) { + @NonNull + public final <@NonNull V> Flowable timeout(@NonNull Function> itemTimeoutIndicator) { return timeout0(null, itemTimeoutIndicator, null); } /** - * Returns a Flowable that mirrors the source Publisher, but that switches to a fallback Publisher if - * an item emitted by the source Publisher doesn't arrive within a window of time after the emission of the - * previous item, where that period of time is measured by a Publisher that is a function of the previous + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, but that switches to a fallback {@link Publisher} if + * an item emitted by the current {@code Flowable} doesn't arrive within a window of time after the emission of the + * previous item, where that period of time is measured by a {@code Publisher} that is a function of the previous * item. *

- * + * *

* Note: The arrival of the first source item is never timed out. *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. - * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * If any of the current {@code Flowable}s violate this, it may throw an + * {@link IllegalStateException} when the current {@code Flowable} completes.
*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code immediate} {@link Scheduler}.
*
@@ -16447,33 +17987,32 @@ public final Flowable timeout(Function> * @param * the timeout value type (ignored) * @param itemTimeoutIndicator - * a function that returns a Publisher, for each item emitted by the source Publisher, that + * a function that returns a {@code Publisher}, for each item emitted by the current {@code Flowable}, that * determines the timeout window for the subsequent item - * @param other - * the fallback Publisher to switch to if the source Publisher times out - * @return a Flowable that mirrors the source Publisher, but switches to mirroring a fallback Publisher - * if an item emitted by the source Publisher takes longer to arrive than the time window defined - * by the selector for the previously emitted item + * @param fallback + * the fallback {@code Publisher} to switch to if the current {@code Flowable} times out + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code itemTimeoutIndicator} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable timeout(Function> itemTimeoutIndicator, Flowable other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(null, itemTimeoutIndicator, other); + public final <@NonNull V> Flowable timeout(@NonNull Function> itemTimeoutIndicator, @NonNull Publisher fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(null, itemTimeoutIndicator, fallback); } /** - * Returns a Flowable that mirrors the source Publisher but applies a timeout policy for each emitted + * Returns a {@code Flowable} that mirrors the current {@code Flowable} but applies a timeout policy for each emitted * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, - * the resulting Publisher terminates and notifies Subscribers of a {@code TimeoutException}. + * the resulting {@code Flowable} terminates and notifies {@link Subscriber}s of a {@link TimeoutException}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -16481,134 +18020,137 @@ public final Flowable timeout(Function> * * @param timeout * maximum duration between emitted items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument. - * @return the source Publisher modified to notify Subscribers of a {@code TimeoutException} in case of a - * timeout + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable timeout(long timeout, TimeUnit timeUnit) { - return timeout0(timeout, timeUnit, null, Schedulers.computation()); + @NonNull + public final Flowable timeout(long timeout, @NonNull TimeUnit unit) { + return timeout0(timeout, unit, null, Schedulers.computation()); } /** - * Returns a Flowable that mirrors the source Publisher but applies a timeout policy for each emitted + * Returns a {@code Flowable} that mirrors the current {@code Flowable} but applies a timeout policy for each emitted * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, - * the source Publisher is disposed and resulting Publisher begins instead to mirror a fallback Publisher. + * the current {@code Flowable} is disposed and the resulting {@code Flowable} begins instead to mirror a fallback {@link Publisher}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. - * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * If any of the current {@code Flowable}s violate this, it may throw an + * {@link IllegalStateException} when the current {@code Flowable} completes.
*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument - * @param other - * the fallback Publisher to use in case of a timeout - * @return the source Publisher modified to switch to the fallback Publisher in case of a timeout + * @param fallback + * the fallback {@code Publisher} to use in case of a timeout + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable timeout(long timeout, TimeUnit timeUnit, Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, timeUnit, other, Schedulers.computation()); + public final Flowable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Publisher fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, fallback, Schedulers.computation()); } /** - * Returns a Flowable that mirrors the source Publisher but applies a timeout policy for each emitted - * item using a specified Scheduler. If the next item isn't emitted within the specified timeout duration - * starting from its predecessor, the source Publisher is disposed and resulting Publisher begins - * instead to mirror a fallback Publisher. + * Returns a {@code Flowable} that mirrors the current {@code Flowable} but applies a timeout policy for each emitted + * item using a specified {@link Scheduler}. If the next item isn't emitted within the specified timeout duration + * starting from its predecessor, the current {@code Flowable} is disposed and the resulting {@code Flowable} begins + * instead to mirror a fallback {@link Publisher}. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. - * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * If any of the current {@code Flowable}s violate this, it may throw an + * {@link IllegalStateException} when the current {@code Flowable} completes.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param scheduler - * the {@link Scheduler} to run the timeout timers on - * @param other - * the Publisher to use as the fallback in case of a timeout - * @return the source Publisher modified so that it will switch to the fallback Publisher in case of a - * timeout + * the {@code Scheduler} to run the timeout timers on + * @param fallback + * the {@code Publisher} to use as the fallback in case of a timeout + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler, Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, timeUnit, other, scheduler); + public final Flowable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Publisher fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, fallback, scheduler); } /** - * Returns a Flowable that mirrors the source Publisher but applies a timeout policy for each emitted - * item, where this policy is governed by a specified Scheduler. If the next item isn't emitted within the - * specified timeout duration starting from its predecessor, the resulting Publisher terminates and - * notifies Subscribers of a {@code TimeoutException}. + * Returns a {@code Flowable} that mirrors the current {@code Flowable} but applies a timeout policy for each emitted + * item, where this policy is governed by a specified {@link Scheduler}. If the next item isn't emitted within the + * specified timeout duration starting from its predecessor, the resulting {@code Flowable} terminates and + * notifies {@link Subscriber}s of a {@link TimeoutException}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param scheduler - * the Scheduler to run the timeout timers on - * @return the source Publisher modified to notify Subscribers of a {@code TimeoutException} in case of a - * timeout + * the {@code Scheduler} to run the timeout timers on + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { - return timeout0(timeout, timeUnit, null, scheduler); + @NonNull + public final Flowable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return timeout0(timeout, unit, null, scheduler); } /** - * Returns a Flowable that mirrors the source Publisher, but notifies Subscribers of a - * {@code TimeoutException} if either the first item emitted by the source Publisher or any subsequent item - * doesn't arrive within time windows defined by other Publishers. + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, but notifies {@link Subscriber}s of a + * {@link TimeoutException} if either the first item emitted by the current {@code Flowable} or any subsequent item + * doesn't arrive within time windows defined by other {@link Publisher}s. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. Both this and the returned {@code Publisher}s * are expected to honor backpressure as well. If any of then violates this rule, it may throw an - * {@code IllegalStateException} when the {@code Publisher} completes.
+ * {@link IllegalStateException} when the {@code Publisher} completes.
*
Scheduler:
*
{@code timeout} does not operate by default on any {@link Scheduler}.
*
@@ -16618,39 +18160,38 @@ public final Flowable timeout(long timeout, TimeUnit timeUnit, Scheduler sche * @param * the subsequent timeout value type (ignored) * @param firstTimeoutIndicator - * a function that returns a Publisher that determines the timeout window for the first source + * a function that returns a {@code Publisher} that determines the timeout window for the first source * item * @param itemTimeoutIndicator - * a function that returns a Publisher for each item emitted by the source Publisher and that + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} and that * determines the timeout window in which the subsequent source item must arrive in order to * continue the sequence - * @return a Flowable that mirrors the source Publisher, but notifies Subscribers of a - * {@code TimeoutException} if either the first item or any subsequent item doesn't arrive within - * the time windows specified by the timeout selectors + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code firstTimeoutIndicator} or {@code itemTimeoutIndicator} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable timeout(Publisher firstTimeoutIndicator, - Function> itemTimeoutIndicator) { - ObjectHelper.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); + public final <@NonNull U, @NonNull V> Flowable timeout(@NonNull Publisher firstTimeoutIndicator, + @NonNull Function> itemTimeoutIndicator) { + Objects.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); return timeout0(firstTimeoutIndicator, itemTimeoutIndicator, null); } /** - * Returns a Flowable that mirrors the source Publisher, but switches to a fallback Publisher if either - * the first item emitted by the source Publisher or any subsequent item doesn't arrive within time windows - * defined by other Publishers. + * Returns a {@code Flowable} that mirrors the current {@code Flowable}, but switches to a fallback {@link Publisher} if either + * the first item emitted by the current {@code Flowable} or any subsequent item doesn't arrive within time windows + * defined by other {@code Publisher}s. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream. The {@code Publisher} * sources are expected to honor backpressure as well. - * If any of the source {@code Publisher}s violate this, it may throw an - * {@code IllegalStateException} when the source {@code Publisher} completes.
+ * If any of the current {@code Flowable}s violate this, it may throw an + * {@link IllegalStateException} when the current {@code Flowable} completes. *
Scheduler:
*
{@code timeout} does not operate by default on any {@link Scheduler}.
*
@@ -16660,108 +18201,107 @@ public final Flowable timeout(Publisher firstTimeoutIndicator, * @param * the subsequent timeout value type (ignored) * @param firstTimeoutIndicator - * a function that returns a Publisher which determines the timeout window for the first source + * a function that returns a {@code Publisher} which determines the timeout window for the first source * item * @param itemTimeoutIndicator - * a function that returns a Publisher for each item emitted by the source Publisher and that + * a function that returns a {@code Publisher} for each item emitted by the current {@code Flowable} and that * determines the timeout window in which the subsequent source item must arrive in order to * continue the sequence - * @param other - * the fallback Publisher to switch to if the source Publisher times out - * @return a Flowable that mirrors the source Publisher, but switches to the {@code other} Publisher if - * either the first item emitted by the source Publisher or any subsequent item doesn't arrive - * within time windows defined by the timeout selectors - * @throws NullPointerException - * if {@code itemTimeoutIndicator} is null + * @param fallback + * the fallback {@code Publisher} to switch to if the current {@code Flowable} times out + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code firstTimeoutIndicator}, {@code itemTimeoutIndicator} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable timeout( - Publisher firstTimeoutIndicator, - Function> itemTimeoutIndicator, - Publisher other) { - ObjectHelper.requireNonNull(firstTimeoutIndicator, "firstTimeoutSelector is null"); - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(firstTimeoutIndicator, itemTimeoutIndicator, other); + public final <@NonNull U, @NonNull V> Flowable timeout( + @NonNull Publisher firstTimeoutIndicator, + @NonNull Function> itemTimeoutIndicator, + @NonNull Publisher fallback) { + Objects.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(firstTimeoutIndicator, itemTimeoutIndicator, fallback); } - private Flowable timeout0(long timeout, TimeUnit timeUnit, Publisher other, + private Flowable timeout0(long timeout, TimeUnit unit, Publisher fallback, Scheduler scheduler) { - ObjectHelper.requireNonNull(timeUnit, "timeUnit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableTimeoutTimed(this, timeout, timeUnit, scheduler, other)); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableTimeoutTimed<>(this, timeout, unit, scheduler, fallback)); } - private Flowable timeout0( + private <@NonNull U, @NonNull V> Flowable timeout0( Publisher firstTimeoutIndicator, - Function> itemTimeoutIndicator, - Publisher other) { - ObjectHelper.requireNonNull(itemTimeoutIndicator, "itemTimeoutIndicator is null"); - return RxJavaPlugins.onAssembly(new FlowableTimeout(this, firstTimeoutIndicator, itemTimeoutIndicator, other)); + Function> itemTimeoutIndicator, + Publisher fallback) { + Objects.requireNonNull(itemTimeoutIndicator, "itemTimeoutIndicator is null"); + return RxJavaPlugins.onAssembly(new FlowableTimeout<>(this, firstTimeoutIndicator, itemTimeoutIndicator, fallback)); } /** - * Returns a Flowable that emits each item emitted by the source Publisher, wrapped in a + * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code timestamp} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
*
* - * @return a Flowable that emits timestamped items from the source Publisher + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> timestamp() { return timestamp(TimeUnit.MILLISECONDS, Schedulers.computation()); } /** - * Returns a Flowable that emits each item emitted by the source Publisher, wrapped in a - * {@link Timed} object whose timestamps are provided by a specified Scheduler. + * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a + * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
This operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ * from the specified {@code Scheduler}. *
* * @param scheduler - * the {@link Scheduler} to use as a time source - * @return a Flowable that emits timestamped items from the source Publisher with timestamps provided by - * the {@code scheduler} + * the {@code Scheduler} to use as a time source + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Flowable> timestamp(Scheduler scheduler) { + @NonNull + public final Flowable> timestamp(@NonNull Scheduler scheduler) { return timestamp(TimeUnit.MILLISECONDS, scheduler); } /** - * Returns a Flowable that emits each item emitted by the source Publisher, wrapped in a + * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
{@code timestamp} does not operate on any particular scheduler but uses the current time @@ -16769,45 +18309,47 @@ public final Flowable> timestamp(Scheduler scheduler) { *
* * @param unit the time unit for the current time - * @return a Flowable that emits timestamped items from the source Publisher + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> timestamp(TimeUnit unit) { + @NonNull + public final Flowable> timestamp(@NonNull TimeUnit unit) { return timestamp(unit, Schedulers.computation()); } /** - * Returns a Flowable that emits each item emitted by the source Publisher, wrapped in a - * {@link Timed} object whose timestamps are provided by a specified Scheduler. + * Returns a {@code Flowable} that emits each item emitted by the current {@code Flowable}, wrapped in a + * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
*
This operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ * from the specified {@code Scheduler}. *
* * @param unit the time unit for the current time * @param scheduler - * the {@link Scheduler} to use as a time source - * @return a Flowable that emits timestamped items from the source Publisher with timestamps provided by - * the {@code scheduler} + * the {@code Scheduler} to use as a time source + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Flowable> timestamp(final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return map(Functions.timestampWith(unit, scheduler)); + public final Flowable> timestamp(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return map(Functions.timestampWith(unit, scheduler)); } /** @@ -16822,107 +18364,108 @@ public final Flowable> timestamp(final TimeUnit unit, final Scheduler s *
*

History: 2.1.7 - experimental * @param the resulting object type - * @param converter the function that receives the current Flowable instance and returns a value + * @param converter the function that receives the current {@code Flowable} instance and returns a value * @return the converted value - * @throws NullPointerException if converter is null + * @throws NullPointerException if {@code converter} is {@code null} * @since 2.2 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.SPECIAL) @SchedulerSupport(SchedulerSupport.NONE) - public final R to(@NonNull FlowableConverter converter) { - return ObjectHelper.requireNonNull(converter, "converter is null").apply(this); + public final <@NonNull R> R to(@NonNull FlowableConverter converter) { + return Objects.requireNonNull(converter, "converter is null").apply(this); } /** - * Returns a Single that emits a single item, a list composed of all the items emitted by the - * finite upstream source Publisher. + * Returns a {@link Single} that emits a single item, a list composed of all the items emitted by the + * finite upstream source {@link Publisher}. *

- * + * *

- * Normally, a Publisher that returns multiple items will do so by invoking its {@link Subscriber}'s - * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior, instructing the - * Publisher to compose a list of all of these items and then to invoke the Subscriber's {@code onNext} - * function once, passing it the entire list, by calling the Publisher's {@code toList} method prior to + * Normally, a {@code Publisher} that returns multiple items will do so by invoking its {@link Subscriber}'s + * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior by having the + * operator compose a list of all of these items and then to invoke the {@link SingleObserver}'s {@code onSuccess} + * method once, passing it the entire list, by calling the {@code Flowable}'s {@code toList} method prior to * calling its {@link #subscribe} method. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toList} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a single item: a List containing all of the items emitted by the source - * Publisher + * @return the new {@code Single} instance * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single> toList() { - return RxJavaPlugins.onAssembly(new FlowableToListSingle>(this)); + return RxJavaPlugins.onAssembly(new FlowableToListSingle<>(this)); } /** - * Returns a Single that emits a single item, a list composed of all the items emitted by the - * finite source Publisher. + * Returns a {@link Single} that emits a single item, a list composed of all the items emitted by the + * finite source {@link Publisher}. *

- * + * *

- * Normally, a Publisher that returns multiple items will do so by invoking its {@link Subscriber}'s - * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior, instructing the - * Publisher to compose a list of all of these items and then to invoke the Subscriber's {@code onNext} - * function once, passing it the entire list, by calling the Publisher's {@code toList} method prior to + * Normally, a {@code Publisher} that returns multiple items will do so by invoking its {@link Subscriber}'s + * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior by having the + * operator compose a list of all of these items and then to invoke the {@link SingleObserver}'s {@code onSuccess} + * method once, passing it the entire list, by calling the {@code Flowable}'s {@code toList} method prior to * calling its {@link #subscribe} method. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toList} does not operate by default on a particular {@link Scheduler}.
*
* * @param capacityHint - * the number of elements expected from the current Flowable - * @return a Flowable that emits a single item: a List containing all of the items emitted by the source - * Publisher + * the number of elements expected from the current {@code Flowable} + * @return the new {@code Single} instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toList(final int capacityHint) { + @NonNull + public final Single> toList(int capacityHint) { ObjectHelper.verifyPositive(capacityHint, "capacityHint"); - return RxJavaPlugins.onAssembly(new FlowableToListSingle>(this, Functions.createArrayList(capacityHint))); + return RxJavaPlugins.onAssembly(new FlowableToListSingle<>(this, Functions.createArrayList(capacityHint))); } /** - * Returns a Single that emits a single item, a list composed of all the items emitted by the - * finite source Publisher. + * Returns a {@link Single} that emits a single item, a list composed of all the items emitted by the + * finite source {@link Publisher}. *

- * + * *

- * Normally, a Publisher that returns multiple items will do so by invoking its {@link Subscriber}'s - * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior, instructing the - * Publisher to compose a list of all of these items and then to invoke the Subscriber's {@code onNext} - * function once, passing it the entire list, by calling the Publisher's {@code toList} method prior to + * Normally, a {@code Publisher} that returns multiple items will do so by invoking its {@link Subscriber}'s + * {@link Subscriber#onNext onNext} method for each such item. You can change this behavior by having the + * operator compose a collection of all of these items and then to invoke the {@link SingleObserver}'s {@code onSuccess} + * method once, passing it the entire collection, by calling the {@code Flowable}'s {@code toList} method prior to * calling its {@link #subscribe} method. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated collection to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toList} does not operate by default on a particular {@link Scheduler}.
@@ -16930,33 +18473,34 @@ public final Single> toList(final int capacityHint) { * * @param the subclass of a collection of Ts * @param collectionSupplier - * the Supplier returning the collection (for each individual Subscriber) to be filled in - * @return a Single that emits a single item: a List containing all of the items emitted by the source - * Publisher + * the {@link Supplier} returning the collection (for each individual {@code Subscriber}) to be filled in + * @return the new {@code Single} instance + * @throws NullPointerException if {@code collectionSupplier} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final > Single toList(Supplier collectionSupplier) { - ObjectHelper.requireNonNull(collectionSupplier, "collectionSupplier is null"); - return RxJavaPlugins.onAssembly(new FlowableToListSingle(this, collectionSupplier)); + @NonNull + public final <@NonNull U extends Collection> Single toList(@NonNull Supplier collectionSupplier) { + Objects.requireNonNull(collectionSupplier, "collectionSupplier is null"); + return RxJavaPlugins.onAssembly(new FlowableToListSingle<>(this, collectionSupplier)); } /** - * Returns a Single that emits a single HashMap containing all items emitted by the finite source Publisher, + * Returns a {@link Single} that emits a single {@link HashMap} containing all items emitted by the finite source {@link Publisher}, * mapped by the keys returned by a specified {@code keySelector} function. *

- * + * *

- * If more than one source item maps to the same key, the HashMap will contain the latest of those items. + * If more than one source item maps to the same key, the {@code HashMap} will contain the latest of those items. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toMap} does not operate by default on a particular {@link Scheduler}.
@@ -16964,35 +18508,35 @@ public final > Single toList(Supplier coll * * @param the key type of the Map * @param keySelector - * the function that extracts the key from a source item to be used in the HashMap - * @return a Single that emits a single item: a HashMap containing the mapped items from the source - * Publisher + * the function that extracts the key from a source item to be used in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toMap(final Function keySelector) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - return collect(HashMapSupplier.asSupplier(), Functions.toMapKeySelector(keySelector)); + public final <@NonNull K> Single> toMap(@NonNull Function keySelector) { + Objects.requireNonNull(keySelector, "keySelector is null"); + return collect(HashMapSupplier.asSupplier(), Functions.toMapKeySelector(keySelector)); } /** - * Returns a Single that emits a single HashMap containing values corresponding to items emitted by the - * finite source Publisher, mapped by the keys returned by a specified {@code keySelector} function. + * Returns a {@link Single} that emits a single {@link HashMap} containing values corresponding to items emitted by the + * finite source {@link Publisher}, mapped by the keys returned by a specified {@code keySelector} function. *

- * + * *

- * If more than one source item maps to the same key, the HashMap will contain a single entry that + * If more than one source item maps to the same key, the {@code HashMap} will contain a single entry that * corresponds to the latest of those items. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toMap} does not operate by default on a particular {@link Scheduler}.
@@ -17001,35 +18545,35 @@ public final Single> toMap(final Function * @param the key type of the Map * @param the value type of the Map * @param keySelector - * the function that extracts the key from a source item to be used in the HashMap + * the function that extracts the key from a source item to be used in the {@code HashMap} * @param valueSelector - * the function that extracts the value from a source item to be used in the HashMap - * @return a Single that emits a single item: a HashMap containing the mapped items from the source - * Publisher + * the function that extracts the value from a source item to be used in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toMap(final Function keySelector, final Function valueSelector) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); - return collect(HashMapSupplier.asSupplier(), Functions.toMapKeyValueSelector(keySelector, valueSelector)); + public final <@NonNull K, @NonNull V> Single> toMap(@NonNull Function keySelector, @NonNull Function valueSelector) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); + return collect(HashMapSupplier.asSupplier(), Functions.toMapKeyValueSelector(keySelector, valueSelector)); } /** - * Returns a Single that emits a single Map, returned by a specified {@code mapFactory} function, that - * contains keys and values extracted from the items emitted by the finite source Publisher. + * Returns a {@link Single} that emits a single {@link Map}, returned by a specified {@code mapFactory} function, that + * contains keys and values extracted from the items emitted by the finite source {@link Publisher}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toMap} does not operate by default on a particular {@link Scheduler}.
@@ -17042,32 +18586,32 @@ public final Single> toMap(final FunctionReactiveX operators documentation: To */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toMap(final Function keySelector, - final Function valueSelector, - final Supplier> mapSupplier) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); + public final <@NonNull K, @NonNull V> Single> toMap(@NonNull Function keySelector, + @NonNull Function valueSelector, + @NonNull Supplier> mapSupplier) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); return collect(mapSupplier, Functions.toMapKeyValueSelector(keySelector, valueSelector)); } /** - * Returns a Single that emits a single HashMap that contains an ArrayList of items emitted by the - * finite source Publisher keyed by a specified {@code keySelector} function. + * Returns a {@link Single} that emits a single {@link HashMap} that contains an {@link ArrayList} of items emitted by the + * finite source {@link Publisher} keyed by a specified {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
*
This operator does not support backpressure as by intent it is requesting and buffering everything.
@@ -17077,15 +18621,16 @@ public final Single> toMap(final Function the key type of the Map * @param keySelector - * the function that extracts the key from the source items to be used as key in the HashMap - * @return a Single that emits a single item: a HashMap that contains an ArrayList of items mapped from - * the source Publisher + * the function that extracts the key from the source items to be used as key in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap(Function keySelector) { + @NonNull + public final <@NonNull K> Single>> toMultimap(@NonNull Function keySelector) { Function valueSelector = Functions.identity(); Supplier>> mapSupplier = HashMapSupplier.asSupplier(); Function> collectionFactory = ArrayListSupplier.asFunction(); @@ -17093,18 +18638,18 @@ public final Single>> toMultimap(Function - * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
@@ -17113,35 +18658,36 @@ public final Single>> toMultimap(Function the key type of the Map * @param the value type of the Map * @param keySelector - * the function that extracts a key from the source items to be used as key in the HashMap + * the function that extracts a key from the source items to be used as key in the {@code HashMap} * @param valueSelector - * the function that extracts a value from the source items to be used as value in the HashMap - * @return a Single that emits a single item: a HashMap that contains an ArrayList of items mapped from - * the source Publisher + * the function that extracts a value from the source items to be used as value in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap(Function keySelector, Function valueSelector) { + @NonNull + public final <@NonNull K, @NonNull V> Single>> toMultimap(@NonNull Function keySelector, @NonNull Function valueSelector) { Supplier>> mapSupplier = HashMapSupplier.asSupplier(); Function> collectionFactory = ArrayListSupplier.asFunction(); return toMultimap(keySelector, valueSelector, mapSupplier, collectionFactory); } /** - * Returns a Single that emits a single Map, returned by a specified {@code mapFactory} function, that + * Returns a {@link Single} that emits a single {@link Map}, returned by a specified {@code mapFactory} function, that * contains a custom collection of values, extracted by a specified {@code valueSelector} function from - * items emitted by the finite source Publisher, and keyed by the {@code keySelector} function. + * items emitted by the finite source {@link Publisher}, and keyed by the {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
@@ -17152,180 +18698,183 @@ public final Single>> toMultimap(FunctionReactiveX operators documentation: To */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap( - final Function keySelector, - final Function valueSelector, - final Supplier>> mapSupplier, - final Function> collectionFactory) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); - ObjectHelper.requireNonNull(mapSupplier, "mapSupplier is null"); - ObjectHelper.requireNonNull(collectionFactory, "collectionFactory is null"); + public final <@NonNull K, @NonNull V> Single>> toMultimap( + @NonNull Function keySelector, + @NonNull Function valueSelector, + @NonNull Supplier>> mapSupplier, + @NonNull Function> collectionFactory) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); + Objects.requireNonNull(mapSupplier, "mapSupplier is null"); + Objects.requireNonNull(collectionFactory, "collectionFactory is null"); return collect(mapSupplier, Functions.toMultimapKeyValueSelector(keySelector, valueSelector, collectionFactory)); } /** - * Returns a Single that emits a single Map, returned by a specified {@code mapFactory} function, that - * contains an ArrayList of values, extracted by a specified {@code valueSelector} function from items - * emitted by the finite source Publisher and keyed by the {@code keySelector} function. + * Returns a {@link Single} that emits a single {@link Map}, returned by a specified {@code mapFactory} function, that + * contains an {@link ArrayList} of values, extracted by a specified {@code valueSelector} function from items + * emitted by the finite source {@link Publisher} and keyed by the {@code keySelector} function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map - * @param the value type of the Map + * @param the key type of the {@code Map} + * @param the value type of the {@code Map} * @param keySelector - * the function that extracts a key from the source items to be used as the key in the Map + * the function that extracts a key from the source items to be used as the key in the {@code Map} * @param valueSelector - * the function that extracts a value from the source items to be used as the value in the Map + * the function that extracts a value from the source items to be used as the value in the {@code Map} * @param mapSupplier - * the function that returns a Map instance to be used - * @return a Single that emits a single item: a Map that contains a list items mapped from the source - * Publisher + * the function that returns a {@code Map} instance to be used + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector}, {@code valueSelector} or {@code mapSupplier} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap( - Function keySelector, - Function valueSelector, - Supplier>> mapSupplier + @NonNull + public final <@NonNull K, @NonNull V> Single>> toMultimap( + @NonNull Function keySelector, + @NonNull Function valueSelector, + @NonNull Supplier>> mapSupplier ) { - return toMultimap(keySelector, valueSelector, mapSupplier, ArrayListSupplier.asFunction()); + return toMultimap(keySelector, valueSelector, mapSupplier, ArrayListSupplier.asFunction()); } /** - * Converts the current Flowable into a non-backpressured {@link Observable}. + * Converts the current {@code Flowable} into a non-backpressured {@link Observable}. *
*
Backpressure:
- *
Observables don't support backpressure thus the current Flowable is consumed in an unbounded - * manner (by requesting Long.MAX_VALUE).
+ *
{@code Observable}s don't support backpressure thus the current {@code Flowable} is consumed in an unbounded + * manner (by requesting {@link Long#MAX_VALUE}).
*
Scheduler:
*
{@code toObservable} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Observable instance + * @return the new {@code Observable} instance * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable toObservable() { - return RxJavaPlugins.onAssembly(new ObservableFromPublisher(this)); + return RxJavaPlugins.onAssembly(new ObservableFromPublisher<>(this)); } /** - * Returns a Single that emits a list that contains the items emitted by the finite source Publisher, in a - * sorted order. Each item emitted by the Publisher must implement {@link Comparable} with respect to all + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the finite source {@link Publisher}, in a + * sorted order. Each item emitted by the {@code Publisher} must implement {@link Comparable} with respect to all * other items in the sequence. * - *

If any item emitted by this Flowable does not implement {@link Comparable} with respect to - * all other items emitted by this Flowable, no items will be emitted and the + *

If any item emitted by this {@code Flowable} does not implement {@code Comparable} with respect to + * all other items emitted by this {@code Flowable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
- * @return a Single that emits a list that contains the items emitted by the source Publisher in - * sorted order + * @return the new {@code Single} instance * @see ReactiveX operators documentation: To */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single> toSortedList() { return toSortedList(Functions.naturalComparator()); } /** - * Returns a Single that emits a list that contains the items emitted by the finite source Publisher, in a + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the finite source {@link Publisher}, in a * sorted order based on a specified comparison function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
* * @param comparator - * a function that compares two items emitted by the source Publisher and returns an Integer + * a function that compares two items emitted by the current {@code Flowable} and returns an {@code int} * that indicates their sort order - * @return a Single that emits a list that contains the items emitted by the source Publisher in - * sorted order + * @return the new {@code Single} instance + * @throws NullPointerException if {@code comparator} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toSortedList(final Comparator comparator) { - ObjectHelper.requireNonNull(comparator, "comparator is null"); + public final Single> toSortedList(@NonNull Comparator comparator) { + Objects.requireNonNull(comparator, "comparator is null"); return toList().map(Functions.listSorter(comparator)); } /** - * Returns a Single that emits a list that contains the items emitted by the finite source Publisher, in a + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the finite source {@link Publisher}, in a * sorted order based on a specified comparison function. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
* * @param comparator - * a function that compares two items emitted by the source Publisher and returns an Integer + * a function that compares two items emitted by the current {@code Flowable} and returns an {@code int} * that indicates their sort order * @param capacityHint - * the initial capacity of the ArrayList used to accumulate items before sorting - * @return a Single that emits a list that contains the items emitted by the source Publisher in - * sorted order + * the initial capacity of the {@link ArrayList} used to accumulate items before sorting + * @return the new {@code Single} instance + * @throws NullPointerException if {@code comparator} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX operators documentation: To * @since 2.0 */ @@ -17333,87 +18882,92 @@ public final Single> toSortedList(final Comparator comparator @NonNull @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toSortedList(final Comparator comparator, int capacityHint) { - ObjectHelper.requireNonNull(comparator, "comparator is null"); + public final Single> toSortedList(@NonNull Comparator comparator, int capacityHint) { + Objects.requireNonNull(comparator, "comparator is null"); return toList(capacityHint).map(Functions.listSorter(comparator)); } /** - * Returns a Flowable that emits a list that contains the items emitted by the finite source Publisher, in a - * sorted order. Each item emitted by the Publisher must implement {@link Comparable} with respect to all + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the finite source {@link Publisher}, in a + * sorted order. Each item emitted by the {@code Publisher} must implement {@link Comparable} with respect to all * other items in the sequence. * - *

If any item emitted by this Flowable does not implement {@link Comparable} with respect to - * all other items emitted by this Flowable, no items will be emitted and the + *

If any item emitted by this {@code Flowable} does not implement {@code Comparable} with respect to + * all other items emitted by this {@code Flowable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. *

- * + * *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Backpressure:
- *
The operator honors backpressure from downstream and consumes the source {@code Publisher} in an + *
The operator honors backpressure from downstream and consumes the current {@code Flowable} in an * unbounded manner (i.e., without applying backpressure to it).
*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
* * @param capacityHint - * the initial capacity of the ArrayList used to accumulate items before sorting - * @return a Flowable that emits a list that contains the items emitted by the source Publisher in - * sorted order + * the initial capacity of the {@link ArrayList} used to accumulate items before sorting + * @return the new {@code Single} instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX operators documentation: To * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single> toSortedList(int capacityHint) { return toSortedList(Functions.naturalComparator(), capacityHint); } /** - * Modifies the source Publisher so that subscribers will cancel it on a specified - * {@link Scheduler}. + * Cancels the current {@code Flowable} asynchronously by invoking {@link Subscription#cancel()} + * on the specified {@link Scheduler}. + *

+ * The operator suppresses signals from the current {@code Flowable} immediately when the + * downstream cancels the flow because the actual cancellation itself could take an arbitrary amount of time + * to take effect and make the flow stop producing items. *

*
Backpressure:
- *
The operator doesn't interfere with backpressure which is determined by the source {@code Publisher}'s backpressure + *
The operator doesn't interfere with backpressure which is determined by the current {@code Flowable}'s backpressure * behavior.
*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to perform cancellation actions on - * @return the source Publisher modified so that its cancellations happen on the specified - * {@link Scheduler} + * the {@code Scheduler} to perform cancellation actions on + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable unsubscribeOn(Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableUnsubscribeOn(this, scheduler)); + public final Flowable unsubscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableUnsubscribeOn<>(this, scheduler)); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each containing {@code count} items. When the source - * Publisher completes or encounters an error, the resulting Publisher emits the current window and - * propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each containing {@code count} items. When the current + * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the current window and + * propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window will only contain one element. The behavior is - * a tradeoff between no-dataloss and ensuring upstream cancellation can happen. + * a trade-off between no-dataloss and ensuring upstream cancellation can happen. *

*
Backpressure:
- *
The operator honors backpressure of its inner and outer subscribers, however, the inner Publisher uses an + *
The operator honors backpressure of its inner and outer subscribers, however, the inner {@code Flowable} uses an * unbounded buffer that may hold at most {@code count} elements.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -17421,33 +18975,33 @@ public final Flowable unsubscribeOn(Scheduler scheduler) { * * @param count * the maximum size of each window before it should be emitted - * @return a Flowable that emits connected, non-overlapping windows, each containing at most - * {@code count} items from the source Publisher - * @throws IllegalArgumentException if either count is non-positive + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> window(long count) { return window(count, count, bufferSize()); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits windows every {@code skip} items, each containing no more than {@code count} items. When - * the source Publisher completes or encounters an error, the resulting Publisher emits the current window - * and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits windows every {@code skip} items, each containing no more than {@code count} items. When + * the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the current window + * and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff between no-dataloss and ensuring upstream cancellation can happen under some race conditions. + * a trade-off between no-dataloss and ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator honors backpressure of its inner and outer subscribers, however, the inner Publisher uses an + *
The operator honors backpressure of its inner and outer subscribers, however, the inner {@code Flowable} uses an * unbounded buffer that may hold at most {@code count} elements.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -17458,33 +19012,33 @@ public final Flowable> window(long count) { * @param skip * how many items need to be skipped before starting a new window. Note that if {@code skip} and * {@code count} are equal this is the same operation as {@link #window(long)}. - * @return a Flowable that emits windows every {@code skip} items containing at most {@code count} items - * from the source Publisher - * @throws IllegalArgumentException if either count or skip is non-positive + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code count} or {@code skip} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> window(long count, long skip) { return window(count, skip, bufferSize()); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits windows every {@code skip} items, each containing no more than {@code count} items. When - * the source Publisher completes or encounters an error, the resulting Publisher emits the current window - * and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits windows every {@code skip} items, each containing no more than {@code count} items. When + * the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the current window + * and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff between no-dataloss and ensuring upstream cancellation can happen under some race conditions. + * a trade-off between no-dataloss and ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator honors backpressure of its inner and outer subscribers, however, the inner Publisher uses an + *
The operator honors backpressure of its inner and outer subscribers, however, the inner {@code Flowable} uses an * unbounded buffer that may hold at most {@code count} elements.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -17497,40 +19051,40 @@ public final Flowable> window(long count, long skip) { * {@code count} are equal this is the same operation as {@link #window(long)}. * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return a Flowable that emits windows every {@code skip} items containing at most {@code count} items - * from the source Publisher - * @throws IllegalArgumentException if either count or skip is non-positive + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code count}, {@code skip} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable> window(long count, long skip, int bufferSize) { ObjectHelper.verifyPositive(skip, "skip"); ObjectHelper.verifyPositive(count, "count"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableWindow(this, count, skip, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableWindow<>(this, count, skip, bufferSize)); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher starts a new window periodically, as determined by the {@code timeskip} argument. It emits - * each window after a fixed timespan, specified by the {@code timespan} argument. When the source - * Publisher completes or Publisher completes or encounters an error, the resulting Publisher emits the - * current window and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} starts a new window periodically, as determined by the {@code timeskip} argument. It emits + * each window after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the + * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor - * backpressure but have an unbounded inner buffer that may lead to {@code OutOfMemoryError} + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor + * backpressure but have an unbounded inner buffer that may lead to {@link OutOfMemoryError} * if left unconsumed.
*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -17542,35 +19096,37 @@ public final Flowable> window(long count, long skip, int bufferSize) * the period of time after which a new window will be created * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments - * @return a Flowable that emits new windows periodically as a fixed timespan elapses + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> window(long timespan, long timeskip, TimeUnit unit) { + @NonNull + public final Flowable> window(long timespan, long timeskip, @NonNull TimeUnit unit) { return window(timespan, timeskip, unit, Schedulers.computation(), bufferSize()); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher starts a new window periodically, as determined by the {@code timeskip} argument. It emits - * each window after a fixed timespan, specified by the {@code timespan} argument. When the source - * Publisher completes or Publisher completes or encounters an error, the resulting Publisher emits the - * current window and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} starts a new window periodically, as determined by the {@code timeskip} argument. It emits + * each window after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the + * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor - * backpressure but have an unbounded inner buffer that may lead to {@code OutOfMemoryError} + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The returned inner {@code Flowable}s honor + * backpressure but have an unbounded inner buffer that may lead to {@link OutOfMemoryError} * if left unconsumed.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -17583,36 +19139,38 @@ public final Flowable> window(long timespan, long timeskip, TimeUnit * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window - * @return a Flowable that emits new windows periodically as a fixed timespan elapses + * the {@code Scheduler} to use when determining the end and start of a window + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> window(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable> window(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return window(timespan, timeskip, unit, scheduler, bufferSize()); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher starts a new window periodically, as determined by the {@code timeskip} argument. It emits - * each window after a fixed timespan, specified by the {@code timespan} argument. When the source - * Publisher completes or Publisher completes or encounters an error, the resulting Publisher emits the - * current window and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} starts a new window periodically, as determined by the {@code timeskip} argument. It emits + * each window after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the + * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor - * backpressure but have an unbounded inner buffer that may lead to {@code OutOfMemoryError} + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The returned inner {@code Flowable}s honor + * backpressure but have an unbounded inner buffer that may lead to {@link OutOfMemoryError} * if left unconsumed.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -17625,42 +19183,44 @@ public final Flowable> window(long timespan, long timeskip, TimeUnit * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window + * the {@code Scheduler} to use when determining the end and start of a window * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return a Flowable that emits new windows periodically as a fixed timespan elapses + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code timespan}, {@code timeskip} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> window(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler, int bufferSize) { + public final Flowable> window(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); ObjectHelper.verifyPositive(timespan, "timespan"); ObjectHelper.verifyPositive(timeskip, "timeskip"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - return RxJavaPlugins.onAssembly(new FlowableWindowTimed(this, timespan, timeskip, unit, scheduler, Long.MAX_VALUE, bufferSize, false)); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + return RxJavaPlugins.onAssembly(new FlowableWindowTimed<>(this, timespan, timeskip, unit, scheduler, Long.MAX_VALUE, bufferSize, false)); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration specified by the - * {@code timespan} argument. When the source Publisher completes or encounters an error, the resulting - * Publisher emits the current window and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration specified by the + * {@code timespan} argument. When the current {@code Flowable} completes or encounters an error, the resulting + * {@code Flowable} emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor * backpressure and may hold up to {@code count} elements at most.
*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -17671,35 +19231,36 @@ public final Flowable> window(long timespan, long timeskip, TimeUnit * new window * @param unit * the unit of time that applies to the {@code timespan} argument - * @return a Flowable that emits connected, non-overlapping windows representing items emitted by the - * source Publisher during fixed, consecutive durations + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> window(long timespan, TimeUnit unit) { + @NonNull + public final Flowable> window(long timespan, @NonNull TimeUnit unit) { return window(timespan, unit, Schedulers.computation(), Long.MAX_VALUE, false); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration as specified by the + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration as specified by the * {@code timespan} argument or a maximum size as specified by the {@code count} argument (whichever is - * reached first). When the source Publisher completes or encounters an error, the resulting Publisher - * emits the current window and propagates the notification from the source Publisher. + * reached first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} + * emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor * backpressure and may hold up to {@code count} elements at most.
*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -17712,37 +19273,38 @@ public final Flowable> window(long timespan, TimeUnit unit) { * the unit of time that applies to the {@code timespan} argument * @param count * the maximum size of each window before it should be emitted - * @return a Flowable that emits connected, non-overlapping windows of items from the source Publisher - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> window(long timespan, TimeUnit unit, + @NonNull + public final Flowable> window(long timespan, @NonNull TimeUnit unit, long count) { return window(timespan, unit, Schedulers.computation(), count, false); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration as specified by the + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration as specified by the * {@code timespan} argument or a maximum size as specified by the {@code count} argument (whichever is - * reached first). When the source Publisher completes or encounters an error, the resulting Publisher - * emits the current window and propagates the notification from the source Publisher. + * reached first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} + * emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor * backpressure and may hold up to {@code count} elements at most.
*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -17756,38 +19318,39 @@ public final Flowable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param restart - * if true, when a window reaches the capacity limit, the timer is restarted as well - * @return a Flowable that emits connected, non-overlapping windows of items from the source Publisher - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * if {@code true}, when a window reaches the capacity limit, the timer is restarted as well + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Flowable> window(long timespan, TimeUnit unit, + @NonNull + public final Flowable> window(long timespan, @NonNull TimeUnit unit, long count, boolean restart) { return window(timespan, unit, Schedulers.computation(), count, restart); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration as specified by the - * {@code timespan} argument. When the source Publisher completes or encounters an error, the resulting - * Publisher emits the current window and propagates the notification from the source Publisher. + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration as specified by the + * {@code timespan} argument. When the current {@code Flowable} completes or encounters an error, the resulting + * {@code Flowable} emits the current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor - * backpressure but have an unbounded inner buffer that may lead to {@code OutOfMemoryError} + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor + * backpressure but have an unbounded inner buffer that may lead to {@link OutOfMemoryError} * if left unconsumed.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -17799,37 +19362,38 @@ public final Flowable> window(long timespan, TimeUnit unit, * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window - * @return a Flowable that emits connected, non-overlapping windows containing items emitted by the - * source Publisher within a fixed duration + * the {@code Scheduler} to use when determining the end and start of a window + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> window(long timespan, TimeUnit unit, - Scheduler scheduler) { + @NonNull + public final Flowable> window(long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler) { return window(timespan, unit, scheduler, Long.MAX_VALUE, false); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration specified by the + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source Publisher completes or encounters an error, the resulting Publisher emits the - * current window and propagates the notification from the source Publisher. + * first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the + * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor * backpressure and may hold up to {@code count} elements at most.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -17843,38 +19407,39 @@ public final Flowable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window - * @return a Flowable that emits connected, non-overlapping windows of items from the source Publisher - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * the {@code Scheduler} to use when determining the end and start of a window + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> window(long timespan, TimeUnit unit, - Scheduler scheduler, long count) { + @NonNull + public final Flowable> window(long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, long count) { return window(timespan, unit, scheduler, count, false); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration specified by the + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source Publisher completes or encounters an error, the resulting Publisher emits the - * current window and propagates the notification from the source Publisher. + * first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the + * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor * backpressure and may hold up to {@code count} elements at most.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -17888,40 +19453,41 @@ public final Flowable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window + * the {@code Scheduler} to use when determining the end and start of a window * @param restart - * if true, when a window reaches the capacity limit, the timer is restarted as well - * @return a Flowable that emits connected, non-overlapping windows of items from the source Publisher - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * if {@code true}, when a window reaches the capacity limit, the timer is restarted as well + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Flowable> window(long timespan, TimeUnit unit, - Scheduler scheduler, long count, boolean restart) { + @NonNull + public final Flowable> window(long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, long count, boolean restart) { return window(timespan, unit, scheduler, count, restart, bufferSize()); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits connected, non-overlapping windows, each of a fixed duration specified by the + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits connected, non-overlapping windows, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source Publisher completes or encounters an error, the resulting Publisher emits the - * current window and propagates the notification from the source Publisher. + * first). When the current {@code Flowable} completes or encounters an error, the resulting {@code Flowable} emits the + * current window and propagates the notification from the current {@code Flowable}. *

- * + * *

* Note that ignoring windows or subscribing later (i.e., on another thread) will result in * so-called window abandonment where a window may not contain any elements. In this case, subsequent * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is - * a tradeoff for ensuring upstream cancellation can happen under some race conditions. + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The operator consumes the source {@code Publisher} in an unbounded manner. - * The returned {@code Publisher} doesn't support backpressure as it uses - * time to control the creation of windows. The returned inner {@code Publisher}s honor + *
The operator consumes the current {@code Flowable} in an unbounded manner. + * The resulting {@code Flowable} doesn't support backpressure as it uses + * time to control the creation of windows. The emitted inner {@code Flowable}s honor * backpressure and may hold up to {@code count} elements at most.
*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -17935,14 +19501,14 @@ public final Flowable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window + * the {@code Scheduler} to use when determining the end and start of a window * @param restart - * if true, when a window reaches the capacity limit, the timer is restarted as well + * if {@code true}, when a window reaches the capacity limit, the timer is restarted as well * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return a Flowable that emits connected, non-overlapping windows of items from the source Publisher - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count}, {@code timespan} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @@ -17950,25 +19516,30 @@ public final Flowable> window(long timespan, TimeUnit unit, @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.CUSTOM) public final Flowable> window( - long timespan, TimeUnit unit, Scheduler scheduler, + long timespan, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, long count, boolean restart, int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); ObjectHelper.verifyPositive(count, "count"); - return RxJavaPlugins.onAssembly(new FlowableWindowTimed(this, timespan, timespan, unit, scheduler, count, bufferSize, restart)); + return RxJavaPlugins.onAssembly(new FlowableWindowTimed<>(this, timespan, timespan, unit, scheduler, count, bufferSize, restart)); } /** - * Returns a Flowable that emits non-overlapping windows of items it collects from the source Publisher + * Returns a {@code Flowable} that emits non-overlapping windows of items it collects from the current {@code Flowable} * where the boundary of each window is determined by the items emitted from a specified boundary-governing - * Publisher. + * {@link Publisher}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The outer Publisher of this operator does not support backpressure as it uses a {@code boundary} Publisher to control data - * flow. The inner Publishers honor backpressure and buffer everything until the boundary signals the next element.
+ *
The outer {@code Publisher} of this operator does not support backpressure as it uses a {@code boundary} {@code Publisher} to control data + * flow. The inner {@code Publisher}s honor backpressure and buffer everything until the boundary signals the next element.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
*
@@ -17976,29 +19547,34 @@ public final Flowable> window( * @param * the window element type (ignored) * @param boundaryIndicator - * a Publisher whose emitted items close and open windows - * @return a Flowable that emits non-overlapping windows of items it collects from the source Publisher - * where the boundary of each window is determined by the items emitted from the {@code boundary} - * Publisher + * a {@code Publisher} whose emitted items close and open windows + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> window(Publisher boundaryIndicator) { + @NonNull + public final <@NonNull B> Flowable> window(@NonNull Publisher boundaryIndicator) { return window(boundaryIndicator, bufferSize()); } /** - * Returns a Flowable that emits non-overlapping windows of items it collects from the source Publisher + * Returns a {@code Flowable} that emits non-overlapping windows of items it collects from the current {@code Flowable} * where the boundary of each window is determined by the items emitted from a specified boundary-governing - * Publisher. + * {@link Publisher}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The outer Publisher of this operator does not support backpressure as it uses a {@code boundary} Publisher to control data - * flow. The inner Publishers honor backpressure and buffer everything until the boundary signals the next element.
+ *
The outer {@code Publisher} of this operator does not support backpressure as it uses a {@code boundary} {@code Publisher} to control data + * flow. The inner {@code Publisher}s honor backpressure and buffer everything until the boundary signals the next element.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
*
@@ -18006,129 +19582,147 @@ public final Flowable> window(Publisher boundaryIndicator) { * @param * the window element type (ignored) * @param boundaryIndicator - * a Publisher whose emitted items close and open windows + * a {@code Publisher} whose emitted items close and open windows * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return a Flowable that emits non-overlapping windows of items it collects from the source Publisher - * where the boundary of each window is determined by the items emitted from the {@code boundary} - * Publisher + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> window(Publisher boundaryIndicator, int bufferSize) { - ObjectHelper.requireNonNull(boundaryIndicator, "boundaryIndicator is null"); + public final <@NonNull B> Flowable> window(@NonNull Publisher boundaryIndicator, int bufferSize) { + Objects.requireNonNull(boundaryIndicator, "boundaryIndicator is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableWindowBoundary(this, boundaryIndicator, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableWindowBoundary<>(this, boundaryIndicator, bufferSize)); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits windows that contain those items emitted by the source Publisher between the time when - * the {@code windowOpenings} Publisher emits an item and when the Publisher returned by + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits windows that contain those items emitted by the current {@code Flowable} between the time when + * the {@code windowOpenings} {@link Publisher} emits an item and when the {@code Publisher} returned by * {@code closingSelector} emits an item. *

- * + * + *

+ * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The outer Publisher of this operator doesn't support backpressure because the emission of new - * inner Publishers are controlled by the {@code windowOpenings} Publisher. - * The inner Publishers honor backpressure and buffer everything until the associated closing - * Publisher signals or completes.
+ *
The outer {@code Publisher} of this operator doesn't support backpressure because the emission of new + * inner {@code Publisher}s are controlled by the {@code windowOpenings} {@code Publisher}. + * The inner {@code Publisher}s honor backpressure and buffer everything until the associated closing + * {@code Publisher} signals or completes.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the window-opening Publisher - * @param the element type of the window-closing Publishers + * @param the element type of the window-opening {@code Publisher} + * @param the element type of the window-closing {@code Publisher}s * @param openingIndicator - * a Publisher that, when it emits an item, causes another window to be created + * a {@code Publisher} that, when it emits an item, causes another window to be created * @param closingIndicator - * a {@link Function} that produces a Publisher for every window created. When this Publisher + * a {@link Function} that produces a {@code Publisher} for every window created. When this {@code Publisher} * emits an item, the associated window is closed and emitted - * @return a Flowable that emits windows of items emitted by the source Publisher that are governed by - * the specified window-governing Publishers + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code openingIndicator} or {@code closingIndicator} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> window( - Publisher openingIndicator, - Function> closingIndicator) { + @NonNull + public final <@NonNull U, @NonNull V> Flowable> window( + @NonNull Publisher openingIndicator, + @NonNull Function> closingIndicator) { return window(openingIndicator, closingIndicator, bufferSize()); } /** - * Returns a Flowable that emits windows of items it collects from the source Publisher. The resulting - * Publisher emits windows that contain those items emitted by the source Publisher between the time when - * the {@code windowOpenings} Publisher emits an item and when the Publisher returned by + * Returns a {@code Flowable} that emits windows of items it collects from the current {@code Flowable}. The resulting + * {@code Flowable} emits windows that contain those items emitted by the current {@code Flowable} between the time when + * the {@code windowOpenings} {@link Publisher} emits an item and when the {@code Publisher} returned by * {@code closingSelector} emits an item. *

- * + * + *

+ * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Backpressure:
- *
The outer Publisher of this operator doesn't support backpressure because the emission of new - * inner Publishers are controlled by the {@code windowOpenings} Publisher. - * The inner Publishers honor backpressure and buffer everything until the associated closing - * Publisher signals or completes.
+ *
The outer {@code Publisher} of this operator doesn't support backpressure because the emission of new + * inner {@code Publisher}s are controlled by the {@code windowOpenings} {@code Publisher}. + * The inner {@code Publisher}s honor backpressure and buffer everything until the associated closing + * {@code Publisher} signals or completes.
*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the window-opening Publisher - * @param the element type of the window-closing Publishers + * @param the element type of the window-opening {@code Publisher} + * @param the element type of the window-closing {@code Publisher}s * @param openingIndicator - * a Publisher that, when it emits an item, causes another window to be created + * a {@code Publisher} that, when it emits an item, causes another window to be created * @param closingIndicator - * a {@link Function} that produces a Publisher for every window created. When this Publisher + * a {@link Function} that produces a {@code Publisher} for every window created. When this {@code Publisher} * emits an item, the associated window is closed and emitted * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return a Flowable that emits windows of items emitted by the source Publisher that are governed by - * the specified window-governing Publishers + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code openingIndicator} or {@code closingIndicator} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.ERROR) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable> window( - Publisher openingIndicator, - Function> closingIndicator, int bufferSize) { - ObjectHelper.requireNonNull(openingIndicator, "openingIndicator is null"); - ObjectHelper.requireNonNull(closingIndicator, "closingIndicator is null"); + public final <@NonNull U, @NonNull V> Flowable> window( + @NonNull Publisher openingIndicator, + @NonNull Function> closingIndicator, int bufferSize) { + Objects.requireNonNull(openingIndicator, "openingIndicator is null"); + Objects.requireNonNull(closingIndicator, "closingIndicator is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new FlowableWindowBoundarySelector(this, openingIndicator, closingIndicator, bufferSize)); + return RxJavaPlugins.onAssembly(new FlowableWindowBoundarySelector<>(this, openingIndicator, closingIndicator, bufferSize)); } /** - * Merges the specified Publisher into this Publisher sequence by using the {@code resultSelector} - * function only when the source Publisher (this instance) emits an item. + * Merges the specified {@link Publisher} into the current {@code Flowable} sequence by using the {@code resultSelector} + * function only when the current {@code Flowable} (this instance) emits an item. + * + *

Note that this operator doesn't emit anything until the other source has produced at + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and + * not when the other source emits, unlike combineLatest). + * If the other source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before the other source has produced at least one value, the sequence completes + * without emission. *

- * + * * *

*
Backpressure:
*
The operator is a pass-through for backpressure: the backpressure support - * depends on the upstream and downstream's backpressure behavior. The other Publisher + * depends on the upstream and downstream's backpressure behavior. The other {@code Publisher} * is consumed in an unbounded fashion.
*
Scheduler:
*
This operator, by default, doesn't run any particular {@link Scheduler}.
*
* - * @param the element type of the other Publisher + * @param the element type of the other {@code Publisher} * @param the result type of the combination * @param other - * the other Publisher + * the other {@code Publisher} * @param combiner - * the function to call when this Publisher emits an item and the other Publisher has already - * emitted an item, to generate the item to be emitted by the resulting Publisher - * @return a Flowable that merges the specified Publisher into this Publisher by using the - * {@code resultSelector} function only when the source Publisher sequence (this instance) emits an - * item + * the function to call when the current {@code Flowable} emits an item and the other {@code Publisher} has already + * emitted an item, to generate the item to be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} or {@code combiner} is {@code null} * @since 2.0 * @see ReactiveX operators documentation: CombineLatest */ @@ -18136,27 +19730,29 @@ public final Flowable> window( @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable withLatestFrom(Publisher other, - BiFunction combiner) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + public final <@NonNull U, @NonNull R> Flowable withLatestFrom(@NonNull Publisher other, + @NonNull BiFunction combiner) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(combiner, "combiner is null"); return RxJavaPlugins.onAssembly(new FlowableWithLatestFrom(this, combiner, other)); } /** - * Combines the value emission from this Publisher with the latest emissions from the - * other Publishers via a function to produce the output item. + * Combines the value emission from the current {@code Flowable} with the latest emissions from the + * other {@link Publisher}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this Publisher emits (and + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *

*
Backpressure:
- *
This operator is a pass-through for backpressure behavior between the source {@code Publisher} - * and the downstream Subscriber. The other {@code Publisher}s are consumed in an unbounded manner.
+ *
This operator is a pass-through for backpressure behavior between the current {@code Flowable} + * and the downstream {@link Subscriber}. The other {@code Publisher}s are consumed in an unbounded manner.
*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
*
@@ -18164,37 +19760,41 @@ public final Flowable withLatestFrom(Publisher other, * @param the first other source's value type * @param the second other source's value type * @param the result value type - * @param source1 the first other Publisher - * @param source2 the second other Publisher - * @param combiner the function called with an array of values from each participating Publisher - * @return the new Publisher instance + * @param source1 the first other {@code Publisher} + * @param source2 the second other {@code Publisher} + * @param combiner the function called with an array of values from each participating {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable withLatestFrom(Publisher source1, Publisher source2, - Function3 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public final <@NonNull T1, @NonNull T2, @NonNull R> Flowable withLatestFrom(@NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Function3 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(combiner, "combiner is null"); Function f = Functions.toFunction(combiner); return withLatestFrom(new Publisher[] { source1, source2 }, f); } /** - * Combines the value emission from this Publisher with the latest emissions from the - * other Publishers via a function to produce the output item. + * Combines the value emission from the current {@code Flowable} with the latest emissions from the + * other {@link Publisher}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this Publisher emits (and + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *

*
Backpressure:
- *
This operator is a pass-through for backpressure behavior between the source {@code Publisher} - * and the downstream Subscriber. The other {@code Publisher}s are consumed in an unbounded manner.
+ *
This operator is a pass-through for backpressure behavior between the current {@code Flowable} + * and the downstream {@link Subscriber}. The other {@code Publisher}s are consumed in an unbounded manner.
*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
*
@@ -18203,41 +19803,45 @@ public final Flowable withLatestFrom(Publisher source1, Publi * @param the second other source's value type * @param the third other source's value type * @param the result value type - * @param source1 the first other Publisher - * @param source2 the second other Publisher - * @param source3 the third other Publisher - * @param combiner the function called with an array of values from each participating Publisher - * @return the new Publisher instance + * @param source1 the first other {@code Publisher} + * @param source2 the second other {@code Publisher} + * @param source3 the third other {@code Publisher} + * @param combiner the function called with an array of values from each participating {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable withLatestFrom( - Publisher source1, Publisher source2, - Publisher source3, - Function4 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public final <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Flowable withLatestFrom( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, + @NonNull Function4 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(combiner, "combiner is null"); Function f = Functions.toFunction(combiner); return withLatestFrom(new Publisher[] { source1, source2, source3 }, f); } /** - * Combines the value emission from this Publisher with the latest emissions from the - * other Publishers via a function to produce the output item. + * Combines the value emission from the current {@code Flowable} with the latest emissions from the + * other {@link Publisher}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this Publisher emits (and + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *

*
Backpressure:
- *
This operator is a pass-through for backpressure behavior between the source {@code Publisher} - * and the downstream Subscriber. The other {@code Publisher}s are consumed in an unbounded manner.
+ *
This operator is a pass-through for backpressure behavior between the current {@code Flowable} + * and the downstream {@link Subscriber}. The other {@code Publisher}s are consumed in an unbounded manner.
*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
*
@@ -18247,139 +19851,148 @@ public final Flowable withLatestFrom( * @param the third other source's value type * @param the fourth other source's value type * @param the result value type - * @param source1 the first other Publisher - * @param source2 the second other Publisher - * @param source3 the third other Publisher - * @param source4 the fourth other Publisher - * @param combiner the function called with an array of values from each participating Publisher - * @return the new Publisher instance + * @param source1 the first other {@code Publisher} + * @param source2 the second other {@code Publisher} + * @param source3 the third other {@code Publisher} + * @param source4 the fourth other {@code Publisher} + * @param combiner the function called with an array of values from each participating {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable withLatestFrom( - Publisher source1, Publisher source2, - Publisher source3, Publisher source4, - Function5 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public final <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Flowable withLatestFrom( + @NonNull Publisher source1, @NonNull Publisher source2, + @NonNull Publisher source3, @NonNull Publisher source4, + @NonNull Function5 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(combiner, "combiner is null"); Function f = Functions.toFunction(combiner); return withLatestFrom(new Publisher[] { source1, source2, source3, source4 }, f); } /** - * Combines the value emission from this Publisher with the latest emissions from the - * other Publishers via a function to produce the output item. + * Combines the value emission from the current {@code Flowable} with the latest emissions from the + * other {@link Publisher}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this Publisher emits (and + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *

*
Backpressure:
- *
This operator is a pass-through for backpressure behavior between the source {@code Publisher} - * and the downstream Subscriber. The other {@code Publisher}s are consumed in an unbounded manner.
+ *
This operator is a pass-through for backpressure behavior between the current {@code Flowable} + * and the downstream {@link Subscriber}. The other {@code Publisher}s are consumed in an unbounded manner.
*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
*
* * @param the result value type * @param others the array of other sources - * @param combiner the function called with an array of values from each participating Publisher - * @return the new Publisher instance + * @param combiner the function called with an array of values from each participating {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code others} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable withLatestFrom(Publisher[] others, Function combiner) { - ObjectHelper.requireNonNull(others, "others is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); - return RxJavaPlugins.onAssembly(new FlowableWithLatestFromMany(this, others, combiner)); + public final <@NonNull R> Flowable withLatestFrom(@NonNull Publisher<@NonNull ?>[] others, @NonNull Function combiner) { + Objects.requireNonNull(others, "others is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return RxJavaPlugins.onAssembly(new FlowableWithLatestFromMany<>(this, others, combiner)); } /** - * Combines the value emission from this Publisher with the latest emissions from the - * other Publishers via a function to produce the output item. + * Combines the value emission from the current {@code Flowable} with the latest emissions from the + * other {@link Publisher}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this Publisher emits (and + * least one value. The resulting emission only happens when the current {@code Flowable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. * *

*
Backpressure:
- *
This operator is a pass-through for backpressure behavior between the source {@code Publisher} - * and the downstream Subscriber. The other {@code Publisher}s are consumed in an unbounded manner.
+ *
This operator is a pass-through for backpressure behavior between the current {@code Flowable} + * and the downstream {@link Subscriber}. The other {@code Publisher}s are consumed in an unbounded manner.
*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
*
* * @param the result value type * @param others the iterable of other sources - * @param combiner the function called with an array of values from each participating Publisher - * @return the new Publisher instance + * @param combiner the function called with an array of values from each participating {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code others} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.PASS_THROUGH) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable withLatestFrom(Iterable> others, Function combiner) { - ObjectHelper.requireNonNull(others, "others is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); - return RxJavaPlugins.onAssembly(new FlowableWithLatestFromMany(this, others, combiner)); + public final <@NonNull R> Flowable withLatestFrom(@NonNull Iterable<@NonNull ? extends Publisher<@NonNull ?>> others, @NonNull Function combiner) { + Objects.requireNonNull(others, "others is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return RxJavaPlugins.onAssembly(new FlowableWithLatestFromMany<>(this, others, combiner)); } /** - * Returns a Flowable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source Publisher and a specified Iterable sequence. + * Returns a {@code Flowable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Flowable} and a specified {@link Iterable} sequence. *

- * + * *

- * Note that the {@code other} Iterable is evaluated as items are observed from the source Publisher; it is + * Note that the {@code other} {@code Iterable} is evaluated as items are observed from the current {@code Flowable}; it is * not pre-consumed. This allows you to zip infinite streams on either side. *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zipWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items in the {@code other} Iterable + * the type of items in the {@code other} {@code Iterable} * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param other - * the Iterable sequence + * the {@code Iterable} sequence * @param zipper - * a function that combines the pairs of items from the Publisher and the Iterable to generate - * the items to be emitted by the resulting Publisher - * @return a Flowable that pairs up values from the source Publisher and the {@code other} Iterable - * sequence and emits the results of {@code zipFunction} applied to these pairs + * a function that combines the pairs of items from the current {@code Flowable} and the {@code Iterable} to generate + * the items to be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable zipWith(Iterable other, BiFunction zipper) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(zipper, "zipper is null"); - return RxJavaPlugins.onAssembly(new FlowableZipIterable(this, other, zipper)); + public final <@NonNull U, @NonNull R> Flowable zipWith(@NonNull Iterable other, @NonNull BiFunction zipper) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(zipper, "zipper is null"); + return RxJavaPlugins.onAssembly(new FlowableZipIterable<>(this, other, zipper)); } /** - * Returns a Flowable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source Publisher and another specified Publisher. + * Returns a {@code Flowable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Flowable} and another specified {@link Publisher}. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if * one of the sources is shorter than the rest while canceling the other sources. Therefore, it @@ -18393,41 +20006,41 @@ public final Flowable zipWith(Iterable other, BiFunction - * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zipWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the {@code other} Publisher + * the type of items emitted by the {@code other} {@code Publisher} * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param other - * the other Publisher + * the other {@code Publisher} * @param zipper - * a function that combines the pairs of items from the two Publishers to generate the items to - * be emitted by the resulting Publisher - * @return a Flowable that pairs up values from the source Publisher and the {@code other} Publisher - * and emits the results of {@code zipFunction} applied to these pairs + * a function that combines the pairs of items from the two {@code Publisher}s to generate the items to + * be emitted by the resulting {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable zipWith(Publisher other, BiFunction zipper) { - ObjectHelper.requireNonNull(other, "other is null"); + public final <@NonNull U, @NonNull R> Flowable zipWith(@NonNull Publisher other, @NonNull BiFunction zipper) { + Objects.requireNonNull(other, "other is null"); return zip(this, other, zipper); } /** - * Returns a Flowable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source Publisher and another specified Publisher. + * Returns a {@code Flowable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Flowable} and another specified {@link Publisher}. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if * one of the sources is shorter than the rest while canceling the other sources. Therefore, it @@ -18441,43 +20054,44 @@ public final Flowable zipWith(Publisher other, BiFunction * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zipWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the {@code other} Publisher + * the type of items emitted by the {@code other} {@code Publisher} * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param other - * the other Publisher + * the other {@code Publisher} * @param zipper - * a function that combines the pairs of items from the two Publishers to generate the items to - * be emitted by the resulting Publisher + * a function that combines the pairs of items from the two {@code Publisher}s to generate the items to + * be emitted by the resulting {@code Flowable} * @param delayError - * if true, errors from the current Flowable or the other Publisher is delayed until both terminate - * @return a Flowable that pairs up values from the source Publisher and the {@code other} Publisher - * and emits the results of {@code zipFunction} applied to these pairs + * if {@code true}, errors from the current {@code Flowable} or the other {@code Publisher} is delayed until both terminate + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable zipWith(Publisher other, - BiFunction zipper, boolean delayError) { + @NonNull + public final <@NonNull U, @NonNull R> Flowable zipWith(@NonNull Publisher other, + @NonNull BiFunction zipper, boolean delayError) { return zip(this, other, zipper, delayError); } /** - * Returns a Flowable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source Publisher and another specified Publisher. + * Returns a {@code Flowable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Flowable} and another specified {@link Publisher}. *

* The operator subscribes to its sources in the order they are specified and completes eagerly if * one of the sources is shorter than the rest while canceling the other sources. Therefore, it @@ -18491,39 +20105,41 @@ public final Flowable zipWith(Publisher other, * use {@link #doOnCancel(Action)} as well or use {@code using()} to do cleanup in case of completion * or cancellation. *

- * + * *

*
Backpressure:
*
The operator expects backpressure from the sources and honors backpressure from the downstream. - * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in MissingBackpressureException, use + * (I.e., zipping with {@link #interval(long, TimeUnit)} may result in {@link MissingBackpressureException}, use * one of the {@code onBackpressureX} to handle similar, backpressure-ignoring sources.
*
Scheduler:
*
{@code zipWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the {@code other} Publisher + * the type of items emitted by the {@code other} {@code Publisher} * @param - * the type of items emitted by the resulting Publisher + * the type of items emitted by the resulting {@code Flowable} * @param other - * the other Publisher + * the other {@code Publisher} * @param zipper - * a function that combines the pairs of items from the two Publishers to generate the items to - * be emitted by the resulting Publisher + * a function that combines the pairs of items from the two {@code Publisher}s to generate the items to + * be emitted by the resulting {@code Flowable} * @param bufferSize * the capacity hint for the buffer in the inner windows * @param delayError - * if true, errors from the current Flowable or the other Publisher is delayed until both terminate - * @return a Flowable that pairs up values from the source Publisher and the {@code other} Publisher - * and emits the results of {@code zipFunction} applied to these pairs + * if {@code true}, errors from the current {@code Flowable} or the other {@code Publisher} is delayed until both terminate + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable zipWith(Publisher other, - BiFunction zipper, boolean delayError, int bufferSize) { + @NonNull + public final <@NonNull U, @NonNull R> Flowable zipWith(@NonNull Publisher other, + @NonNull BiFunction zipper, boolean delayError, int bufferSize) { return zip(this, other, zipper, delayError, bufferSize); } @@ -18531,68 +20147,71 @@ public final Flowable zipWith(Publisher other, // Fluent test support, super handy and reduces test preparation boilerplate // ------------------------------------------------------------------------- /** - * Creates a TestSubscriber that requests Long.MAX_VALUE and subscribes - * it to this Flowable. + * Creates a {@link TestSubscriber} that requests {@link Long#MAX_VALUE} and subscribes + * it to this {@code Flowable}. *
*
Backpressure:
- *
The returned TestSubscriber consumes this Flowable in an unbounded fashion.
+ *
The returned {@code TestSubscriber} consumes this {@code Flowable} in an unbounded fashion.
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new TestSubscriber instance + * @return the new {@code TestSubscriber} instance * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestSubscriber test() { // NoPMD - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); subscribe(ts); return ts; } /** - * Creates a TestSubscriber with the given initial request amount and subscribes - * it to this Flowable. + * Creates a {@link TestSubscriber} with the given initial request amount and subscribes + * it to this {@code Flowable}. *
*
Backpressure:
- *
The returned TestSubscriber requests the given {@code initialRequest} amount upfront.
+ *
The returned {@code TestSubscriber} requests the given {@code initialRequest} amount upfront.
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
* @param initialRequest the initial request amount, positive - * @return the new TestSubscriber instance + * @return the new {@code TestSubscriber} instance * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestSubscriber test(long initialRequest) { // NoPMD - TestSubscriber ts = new TestSubscriber(initialRequest); + TestSubscriber ts = new TestSubscriber<>(initialRequest); subscribe(ts); return ts; } /** - * Creates a TestSubscriber with the given initial request amount, + * Creates a {@link TestSubscriber} with the given initial request amount, * optionally cancels it before the subscription and subscribes - * it to this Flowable. + * it to this {@code Flowable}. *
*
Backpressure:
- *
The returned TestSubscriber requests the given {@code initialRequest} amount upfront.
+ *
The returned {@code TestSubscriber} requests the given {@code initialRequest} amount upfront.
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
* @param initialRequest the initial request amount, positive - * @param cancel should the TestSubscriber be canceled before the subscription? - * @return the new TestSubscriber instance + * @param cancel should the {@code TestSubscriber} be canceled before the subscription? + * @return the new {@code TestSubscriber} instance * @since 2.0 */ @CheckReturnValue @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestSubscriber test(long initialRequest, boolean cancel) { // NoPMD - TestSubscriber ts = new TestSubscriber(initialRequest); + TestSubscriber ts = new TestSubscriber<>(initialRequest); if (cancel) { ts.cancel(); } @@ -18600,4 +20219,681 @@ public final TestSubscriber test(long initialRequest, boolean cancel) { // No return ts; } + // ------------------------------------------------------------------------- + // JDK 8 Support + // ------------------------------------------------------------------------- + + /** + * Converts the existing value of the provided optional into a {@link #just(Object)} + * or an empty optional into an {@link #empty()} {@code Flowable} instance. + *

+ * + *

+ * Note that the operator takes an already instantiated optional reference and does not + * by any means create this original optional. If the optional is to be created per + * consumer upon subscription, use {@link #defer(Supplier)} around {@code fromOptional}: + *


+     * Flowable.defer(() -> Flowable.fromOptional(createOptional()));
+     * 
+ *
+ *
Backpressure:
+ *
The returned {@code Flowable} supports backpressure.
+ *
Scheduler:
+ *
{@code fromOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the optional value + * @param optional the optional value to convert into a {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code optional} is {@code null} + * @since 3.0.0 + * @see #just(Object) + * @see #empty() + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable<@NonNull T> fromOptional(@NonNull Optional optional) { + Objects.requireNonNull(optional, "optional is null"); + return optional.map(Flowable::just).orElseGet(Flowable::empty); + } + + /** + * Signals the completion value or error of the given (hot) {@link CompletionStage}-based asynchronous calculation. + *

+ * + *

+ * Note that the operator takes an already instantiated, running or terminated {@code CompletionStage}. + * If the {@code CompletionStage} is to be created per consumer upon subscription, use {@link #defer(Supplier)} + * around {@code fromCompletionStage}: + *


+     * Flowable.defer(() -> Flowable.fromCompletionStage(createCompletionStage()));
+     * 
+ *

+ * If the {@code CompletionStage} completes with {@code null}, a {@link NullPointerException} is signaled. + *

+ * Canceling the flow can't cancel the execution of the {@code CompletionStage} because {@code CompletionStage} + * itself doesn't support cancellation. Instead, the operator detaches from the {@code CompletionStage}. + *

+ *
Backpressure:
+ *
The returned {@code Flowable} supports backpressure and caches the completion value until the + * downstream is ready to receive it.
+ *
Scheduler:
+ *
{@code fromCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code CompletionStage} + * @param stage the {@code CompletionStage} to convert to {@code Flowable} and signal its terminal value or error + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code stage} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable<@NonNull T> fromCompletionStage(@NonNull CompletionStage stage) { + Objects.requireNonNull(stage, "stage is null"); + return RxJavaPlugins.onAssembly(new FlowableFromCompletionStage<>(stage)); + } + + /** + * Converts a {@link Stream} into a finite {@code Flowable} and emits its items in the sequence. + *

+ * + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. Any exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #fromIterable(Iterable)}: + *


+     * Stream<T> stream = ...
+     * Flowable.fromIterable(stream::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * IntStream intStream = IntStream.rangeClosed(1, 10);
+     * Flowable.fromStream(intStream.boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and iterates the given {@code Stream} + * on demand (i.e., when requested).
+ *
Scheduler:
+ *
{@code fromStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the source {@code Stream} + * @param stream the {@code Stream} of values to emit + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code stream} is {@code null} + * @since 3.0.0 + * @see #fromIterable(Iterable) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable<@NonNull T> fromStream(@NonNull Stream stream) { + Objects.requireNonNull(stream, "stream is null"); + return RxJavaPlugins.onAssembly(new FlowableFromStream<>(stream)); + } + + /** + * Maps each upstream value into an {@link Optional} and emits the contained item if not empty. + *

+ * + * + *

+ *
Backpressure:
+ *
The operator is a pass-through for downstream requests but issues {@code request(1)} whenever the + * mapped {@code Optional} is empty.
+ *
Scheduler:
+ *
{@code mapOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the non-{@code null} output type + * @param mapper the function that receives the upstream item and should return a non-empty {@code Optional} + * to emit as the output or an empty {@code Optional} to skip to the next upstream value + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #map(Function) + * @see #filter(Predicate) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Flowable mapOptional(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new FlowableMapOptional<>(this, mapper)); + } + + /** + * Collects the finite upstream's values into a container via a {@link Stream} {@link Collector} callback set and emits + * it as the success result. + *

+ * + * + *

+ *
Backpressure:
+ *
The operator consumes the upstream in an unbounded manner.
+ *
Scheduler:
+ *
{@code collect} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the non-{@code null} result type + * @param the intermediate container type used for the accumulation + * @param collector the interface defining the container supplier, accumulator and finisher functions; + * see {@link Collectors} for some standard implementations + * @return the new {@link Single} instance + * @throws NullPointerException if {@code collector} is {@code null} + * @since 3.0.0 + * @see Collectors + * @see #collect(Supplier, BiConsumer) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R, @Nullable A> Single collect(@NonNull Collector collector) { + Objects.requireNonNull(collector, "collector is null"); + return RxJavaPlugins.onAssembly(new FlowableCollectWithCollectorSingle<>(this, collector)); + } + + /** + * Signals the first upstream item (or the default item if the upstream is empty) via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).firstStage(Optional.empty());
+     * 
+ *
+ *
Backpressure:
+ *
The operator requests one item from upstream and then when received, cancels the upstream.
+ *
Scheduler:
+ *
{@code firstStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #firstOrErrorStage() + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage firstStage(@Nullable T defaultItem) { + return subscribeWith(new FlowableFirstStageSubscriber<>(true, defaultItem)); + } + + /** + * Signals the only expected upstream item (or the default item if the upstream is empty) + * or signals {@link IllegalArgumentException} if the upstream has more than one item + * via a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).singleStage(Optional.empty());
+     * 
+ *
+ *
Backpressure:
+ *
The operator requests two items from upstream and then when more than one item is received, cancels the upstream.
+ *
Scheduler:
+ *
{@code singleStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #singleOrErrorStage() + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage singleStage(@Nullable T defaultItem) { + return subscribeWith(new FlowableSingleStageSubscriber<>(true, defaultItem)); + } + + /** + * Signals the last upstream item (or the default item if the upstream is empty) via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).lastStage(Optional.empty());
+     * 
+ *
+ *
Backpressure:
+ *
The operator requests an unbounded number of items from the upstream.
+ *
Scheduler:
+ *
{@code lastStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #lastOrErrorStage() + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage lastStage(@Nullable T defaultItem) { + return subscribeWith(new FlowableLastStageSubscriber<>(true, defaultItem)); + } + + /** + * Signals the first upstream item or a {@link NoSuchElementException} if the upstream is empty via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Backpressure:
+ *
The operator requests one item from upstream and then when received, cancels the upstream.
+ *
Scheduler:
+ *
{@code firstOrErrorStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #firstStage(Object) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage firstOrErrorStage() { + return subscribeWith(new FlowableFirstStageSubscriber<>(false, null)); + } + + /** + * Signals the only expected upstream item, a {@link NoSuchElementException} if the upstream is empty + * or signals {@link IllegalArgumentException} if the upstream has more than one item + * via a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Backpressure:
+ *
The operator requests two items from upstream and then when more than one item is received, cancels the upstream.
+ *
Scheduler:
+ *
{@code singleOrErrorStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #singleStage(Object) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage singleOrErrorStage() { + return subscribeWith(new FlowableSingleStageSubscriber<>(false, null)); + } + + /** + * Signals the last upstream item or a {@link NoSuchElementException} if the upstream is empty via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Backpressure:
+ *
The operator requests an unbounded number of items from the upstream.
+ *
Scheduler:
+ *
{@code lastOrErrorStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #lastStage(Object) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage lastOrErrorStage() { + return subscribeWith(new FlowableLastStageSubscriber<>(false, null)); + } + + /** + * Creates a sequential {@link Stream} to consume or process this {@code Flowable} in a blocking manner via + * the Java {@code Stream} API. + *

+ * + *

+ * Cancellation of the upstream is done via {@link Stream#close()}, therefore, it is strongly recommended the + * consumption is performed within a try-with-resources construct: + *


+     * Flowable<Integer> source = Flowable.range(1, 10)
+     *        .subscribeOn(Schedulers.computation());
+     *
+     * try (Stream<Integer> stream = source.blockingStream()) {
+     *     stream.limit(3).forEach(System.out::println);
+     * }
+     * 
+ *
+ *
Backpressure:
+ *
The operator requests {@link #bufferSize()} amount upfront and 75% of it after each 75% of the amount received.
+ *
Scheduler:
+ *
{@code blockingStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @return the new {@code Stream} instance + * @since 3.0.0 + * @see #blockingStream(int) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Stream blockingStream() { + return blockingStream(bufferSize()); + } + + /** + * Creates a sequential {@link Stream} to consume or process this {@code Flowable} in a blocking manner via + * the Java {@code Stream} API. + *

+ * + *

+ * Cancellation of the upstream is done via {@link Stream#close()}, therefore, it is strongly recommended the + * consumption is performed within a try-with-resources construct: + *


+     * Flowable<Integer> source = Flowable.range(1, 10)
+     *        .subscribeOn(Schedulers.computation());
+     *
+     * try (Stream<Integer> stream = source.blockingStream(4)) {
+     *     stream.limit(3).forEach(System.out::println);
+     * }
+     * 
+ *
+ *
Backpressure:
+ *
The operator requests the given {@code prefetch} amount upfront and 75% of it after each 75% of the amount received.
+ *
Scheduler:
+ *
{@code blockingStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param prefetch the number of items to request from the upstream to limit the number of + * in-flight items and item generation. + * @return the new {@code Stream} instance + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Stream blockingStream(int prefetch) { + Iterator iterator = blockingIterable(prefetch).iterator(); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false) + .onClose(((Disposable) iterator)::dispose); + } + + /** + * Maps each upstream item into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. Any exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #concatMapIterable(Function)}: + *


+     * source.concatMapIterable(v -> createStream(v)::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.concatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors the downstream backpressure and consumes the inner stream only on demand. The operator + * prefetches {@link #bufferSize} items of the upstream (then 75% of it after the 75% received) + * and caches them until they are ready to be mapped into {@code Stream}s + * after the current {@code Stream} has been consumed.
+ *
Scheduler:
+ *
{@code concatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #concatMap(Function) + * @see #concatMapIterable(Function) + * @see #concatMapStream(Function, int) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Flowable concatMapStream(@NonNull Function> mapper) { + return flatMapStream(mapper, bufferSize()); + } + + /** + * Maps each upstream item into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. Any exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #concatMapIterable(Function, int)}: + *


+     * source.concatMapIterable(v -> createStream(v)::iterator, 32);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.concatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed(), 32);
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors the downstream backpressure and consumes the inner stream only on demand. The operator + * prefetches the given amount of upstream items and caches them until they are ready to be mapped into {@code Stream}s + * after the current {@code Stream} has been consumed.
+ *
Scheduler:
+ *
{@code concatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @param prefetch the number of upstream items to request upfront, then 75% of this amount after each 75% upstream items received + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 + * @see #concatMap(Function, int) + * @see #concatMapIterable(Function, int) + * @see #flatMapStream(Function, int) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Flowable concatMapStream(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(prefetch, "prefetch"); + return RxJavaPlugins.onAssembly(new FlowableFlatMapStream<>(this, mapper, prefetch)); + } + + /** + * Maps each upstream item into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. Any exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flatMapIterable(Function)}: + *


+     * source.flatMapIterable(v -> createStream(v)::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors the downstream backpressure and consumes the inner stream only on demand. The operator + * prefetches {@link #bufferSize} items of the upstream (then 75% of it after the 75% received) + * and caches them until they are ready to be mapped into {@code Stream}s + * after the current {@code Stream} has been consumed.
+ *
Scheduler:
+ *
{@code flatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #flatMap(Function) + * @see #flatMapIterable(Function) + * @see #flatMapStream(Function, int) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Flowable flatMapStream(@NonNull Function> mapper) { + return flatMapStream(mapper, bufferSize()); + } + + /** + * Maps each upstream item into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. Any exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flatMapIterable(Function, int)}: + *


+     * source.flatMapIterable(v -> createStream(v)::iterator, 32);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed(), 32);
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors the downstream backpressure and consumes the inner stream only on demand. The operator + * prefetches the given amount of upstream items and caches them until they are ready to be mapped into {@code Stream}s + * after the current {@code Stream} has been consumed.
+ *
Scheduler:
+ *
{@code flatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @param prefetch the number of upstream items to request upfront, then 75% of this amount after each 75% upstream items received + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 + * @see #flatMap(Function, int) + * @see #flatMapIterable(Function, int) + * @see #concatMapStream(Function, int) + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Flowable flatMapStream(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(prefetch, "prefetch"); + return RxJavaPlugins.onAssembly(new FlowableFlatMapStream<>(this, mapper, prefetch)); + } } diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableConverter.java b/src/main/java/io/reactivex/rxjava3/core/FlowableConverter.java index 0736dc189d..1db19b9144 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableConverter.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableConverter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,20 +16,20 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Convenience interface and callback used by the {@link Flowable#to} operator to turn a Flowable into another + * Convenience interface and callback used by the {@link Flowable#to} operator to turn a {@link Flowable} into another * value fluently. *

History: 2.1.7 - experimental * @param the upstream type * @param the output type * @since 2.2 */ -public interface FlowableConverter { +@FunctionalInterface +public interface FlowableConverter<@NonNull T, @NonNull R> { /** - * Applies a function to the upstream Flowable and returns a converted value of type {@code R}. + * Applies a function to the upstream {@link Flowable} and returns a converted value of type {@code R}. * - * @param upstream the upstream Flowable instance + * @param upstream the upstream {@code Flowable} instance * @return the converted value */ - @NonNull R apply(@NonNull Flowable upstream); } diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableEmitter.java b/src/main/java/io/reactivex/rxjava3/core/FlowableEmitter.java index 8f66b7bbee..5ad6850389 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableEmitter.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableEmitter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.functions.Cancellable; +import io.reactivex.rxjava3.functions.*; /** * Abstraction over a Reactive Streams {@link org.reactivestreams.Subscriber} that allows associating @@ -48,19 +48,21 @@ * * @param the value type to emit */ -public interface FlowableEmitter extends Emitter { +public interface FlowableEmitter<@NonNull T> extends Emitter { /** * Sets a Disposable on this emitter; any previous {@link Disposable} * or {@link Cancellable} will be disposed/cancelled. - * @param d the disposable, null is allowed + *

This method is thread-safe. + * @param d the disposable, {@code null} is allowed */ void setDisposable(@Nullable Disposable d); /** - * Sets a Cancellable on this emitter; any previous {@link Disposable} - * or {@link Cancellable} will be disposed/cancelled. - * @param c the cancellable resource, null is allowed + * Sets a {@link Cancellable} on this emitter; any previous {@link Disposable} + * or {@code Cancellable} will be disposed/cancelled. + *

This method is thread-safe. + * @param c the {@code Cancellable} resource, {@code null} is allowed */ void setCancellable(@Nullable Cancellable c); @@ -81,19 +83,19 @@ public interface FlowableEmitter extends Emitter { boolean isCancelled(); /** - * Ensures that calls to onNext, onError and onComplete are properly serialized. - * @return the serialized FlowableEmitter + * Ensures that calls to {@code onNext}, {@code onError} and {@code onComplete} are properly serialized. + * @return the serialized {@link FlowableEmitter} */ @NonNull FlowableEmitter serialize(); /** - * Attempts to emit the specified {@code Throwable} error if the downstream + * Attempts to emit the specified {@link Throwable} error if the downstream * hasn't cancelled the sequence or is otherwise terminated, returning false * if the emission is not allowed to happen due to lifecycle restrictions. *

- * Unlike {@link #onError(Throwable)}, the {@code RxJavaPlugins.onError} is not called - * if the error could not be delivered. + * Unlike {@link #onError(Throwable)}, the {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable) RxjavaPlugins.onError} + * is not called if the error could not be delivered. *

History: 2.1.1 - experimental * @param t the throwable error to signal if possible * @return true if successful, false if the downstream is not able to accept further diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java index 93d125de2f..6c5263b779 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableOnSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,22 +10,24 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link FlowableEmitter} instance that allows pushing + * a {@link FlowableEmitter} instance that allows pushing * events in a backpressure-safe and cancellation-safe manner. * * @param the value type pushed */ -public interface FlowableOnSubscribe { +@FunctionalInterface +public interface FlowableOnSubscribe<@NonNull T> { /** - * Called for each Subscriber that subscribes. - * @param emitter the safe emitter instance, never null + * Called for each {@link org.reactivestreams.Subscriber Subscriber} that subscribes. + * @param emitter the safe emitter instance, never {@code null} * @throws Throwable on error */ void subscribe(@NonNull FlowableEmitter emitter) throws Throwable; diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableOperator.java b/src/main/java/io/reactivex/rxjava3/core/FlowableOperator.java index 8ce0ab8f7d..aa2d5ff0e7 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableOperator.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableOperator.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,18 +18,19 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to map/wrap a downstream subscriber to an upstream subscriber. + * Interface to map/wrap a downstream {@link Subscriber} to an upstream {@code Subscriber}. * * @param the value type of the downstream * @param the value type of the upstream */ -public interface FlowableOperator { +@FunctionalInterface +public interface FlowableOperator<@NonNull Downstream, @NonNull Upstream> { /** - * Applies a function to the child Subscriber and returns a new parent Subscriber. - * @param subscriber the child Subscriber instance - * @return the parent Subscriber instance - * @throws Exception on failure + * Applies a function to the child {@link Subscriber} and returns a new parent {@code Subscriber}. + * @param subscriber the child {@code Subscriber} instance + * @return the parent {@code Subscriber} instance + * @throws Throwable on failure */ @NonNull - Subscriber apply(@NonNull Subscriber subscriber) throws Exception; + Subscriber apply(@NonNull Subscriber subscriber) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java b/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java index 3b0a80cf9a..f97eadbd37 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,14 +18,16 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Represents a Reactive-Streams inspired Subscriber that is RxJava 2 only - * and weakens rules §1.3 and §3.9 of the specification for gaining performance. + * Represents a Reactive-Streams inspired {@link Subscriber} that is RxJava 3 only + * and weakens the Reactive Streams rules §1.3 + * and §3.9 of the specification + * for gaining performance. * *

History: 2.0.7 - experimental; 2.1 - beta * @param the value type * @since 2.2 */ -public interface FlowableSubscriber extends Subscriber { +public interface FlowableSubscriber<@NonNull T> extends Subscriber { /** * Implementors of this method should make sure everything that needs diff --git a/src/main/java/io/reactivex/rxjava3/core/FlowableTransformer.java b/src/main/java/io/reactivex/rxjava3/core/FlowableTransformer.java index ecee751040..fe51a2d5d7 100644 --- a/src/main/java/io/reactivex/rxjava3/core/FlowableTransformer.java +++ b/src/main/java/io/reactivex/rxjava3/core/FlowableTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,17 +18,18 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to compose Flowables. + * Interface to compose {@link Flowable}s. * * @param the upstream value type * @param the downstream value type */ -public interface FlowableTransformer { +@FunctionalInterface +public interface FlowableTransformer<@NonNull Upstream, @NonNull Downstream> { /** - * Applies a function to the upstream Flowable and returns a Publisher with + * Applies a function to the upstream {@link Flowable} and returns a {@link Publisher} with * optionally different element type. - * @param upstream the upstream Flowable instance - * @return the transformed Publisher instance + * @param upstream the upstream {@code Flowable} instance + * @return the transformed {@code Publisher} instance */ @NonNull Publisher apply(@NonNull Flowable upstream); diff --git a/src/main/java/io/reactivex/rxjava3/core/Maybe.java b/src/main/java/io/reactivex/rxjava3/core/Maybe.java index 9bcf753e85..8ae4137c83 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Maybe.java +++ b/src/main/java/io/reactivex/rxjava3/core/Maybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,25 +13,28 @@ package io.reactivex.rxjava3.core; -import java.util.NoSuchElementException; +import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.observers.BlockingMultiObserver; +import io.reactivex.rxjava3.internal.jdk8.*; +import io.reactivex.rxjava3.internal.observers.*; import io.reactivex.rxjava3.internal.operators.flowable.*; import io.reactivex.rxjava3.internal.operators.maybe.*; import io.reactivex.rxjava3.internal.operators.mixed.*; +import io.reactivex.rxjava3.internal.operators.observable.ObservableElementAtMaybe; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.schedulers.*; /** * The {@code Maybe} class represents a deferred computation and emission of a single value, no value at all or an exception. @@ -44,10 +47,10 @@ * onSubscribe (onSuccess | onError | onComplete)? *

*

- * Note that {@code onSuccess}, {@code onError} and {@code onComplete} are mutually exclusive events; unlike {@code Observable}, + * Note that {@code onSuccess}, {@code onError} and {@code onComplete} are mutually exclusive events; unlike {@link Observable}, * {@code onSuccess} is never followed by {@code onError} or {@code onComplete}. *

- * Like {@link Observable}, a running {@code Maybe} can be stopped through the {@link Disposable} instance + * Like {@code Observable}, a running {@code Maybe} can be stopped through the {@link Disposable} instance * provided to consumers through {@link MaybeObserver#onSubscribe}. *

* Like an {@code Observable}, a {@code Maybe} is lazy, can be either "hot" or "cold", synchronous or @@ -59,7 +62,7 @@ *

* *

- * See {@link Flowable} or {@link Observable} for the + * See {@link Flowable} or {@code Observable} for the * implementation of the Reactive Pattern for a stream or vector of values. *

* Example: @@ -107,32 +110,33 @@ * @since 2.0 * @see io.reactivex.rxjava3.observers.DisposableMaybeObserver */ -public abstract class Maybe implements MaybeSource { +public abstract class Maybe<@NonNull T> implements MaybeSource { /** - * Runs multiple MaybeSources and signals the events of the first one that signals (disposing - * the rest). + * Runs multiple {@link MaybeSource}s provided by an {@link Iterable} sequence and + * signals the events of the first one that signals (disposing the rest). *

- * + * *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Iterable sequence of sources. A subscription to each source will - * occur in the same order as in the Iterable. - * @return the new Maybe instance + * @param sources the {@code Iterable} sequence of sources. A subscription to each source will + * occur in the same order as in the {@code Iterable}. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe amb(final Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new MaybeAmb(null, sources)); + public static <@NonNull T> Maybe amb(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new MaybeAmb<>(null, sources)); } /** - * Runs multiple MaybeSources and signals the events of the first one that signals (disposing + * Runs multiple {@link MaybeSource}s and signals the events of the first one that signals (disposing * the rest). *

* @@ -143,24 +147,29 @@ public static Maybe amb(final Iterable * @param the value type * @param sources the array of sources. A subscription to each source will * occur in the same order as in the array. - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Maybe ambArray(final MaybeSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Maybe ambArray(@NonNull MaybeSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } if (sources.length == 1) { - return wrap((MaybeSource)sources[0]); + @SuppressWarnings("unchecked") + MaybeSource source = (MaybeSource)sources[0]; + return wrap(source); } - return RxJavaPlugins.onAssembly(new MaybeAmb(sources, null)); + return RxJavaPlugins.onAssembly(new MaybeAmb<>(sources, null)); } /** - * Concatenate the single values, in a non-overlapping fashion, of the MaybeSource sources provided by - * an Iterable sequence. + * Concatenate the single values, in a non-overlapping fashion, of the {@link MaybeSource} sources provided by + * an {@link Iterable} sequence as a {@link Flowable} sequence. *

* *

@@ -170,22 +179,23 @@ public static Maybe ambArray(final MaybeSource... sources) { *
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Iterable sequence of MaybeSource instances - * @return the new Flowable instance + * @param sources the {@code Iterable} sequence of {@code MaybeSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new MaybeConcatIterable(sources)); + public static <@NonNull T> Flowable concat(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new MaybeConcatIterable<>(sources)); } /** - * Returns a Flowable that emits the items emitted by two MaybeSources, one after the other. + * Returns a {@link Flowable} that emits the items emitted by two {@link MaybeSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -195,27 +205,27 @@ public static Flowable concat(Iterable * * @param the common value type * @param source1 - * a MaybeSource to be concatenated + * a {@code MaybeSource} to be concatenated * @param source2 - * a MaybeSource to be concatenated - * @return a Flowable that emits items emitted by the two source MaybeSources, one after the other. + * a {@code MaybeSource} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Concat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concat(MaybeSource source1, MaybeSource source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T> Flowable concat(@NonNull MaybeSource source1, @NonNull MaybeSource source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return concatArray(source1, source2); } /** - * Returns a Flowable that emits the items emitted by three MaybeSources, one after the other. + * Returns a {@link Flowable} that emits the items emitted by three {@link MaybeSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -225,31 +235,31 @@ public static Flowable concat(MaybeSource source1, MaybeSour * * @param the common value type * @param source1 - * a MaybeSource to be concatenated + * a {@code MaybeSource} to be concatenated * @param source2 - * a MaybeSource to be concatenated + * a {@code MaybeSource} to be concatenated * @param source3 - * a MaybeSource to be concatenated - * @return a Flowable that emits items emitted by the three source MaybeSources, one after the other. + * a {@code MaybeSource} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Concat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concat( - MaybeSource source1, MaybeSource source2, MaybeSource source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T> Flowable concat( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return concatArray(source1, source2, source3); } /** - * Returns a Flowable that emits the items emitted by four MaybeSources, one after the other. + * Returns a {@link Flowable} that emits the items emitted by four {@link MaybeSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -259,85 +269,89 @@ public static Flowable concat( * * @param the common value type * @param source1 - * a MaybeSource to be concatenated + * a {@code MaybeSource} to be concatenated * @param source2 - * a MaybeSource to be concatenated + * a {@code MaybeSource} to be concatenated * @param source3 - * a MaybeSource to be concatenated + * a {@code MaybeSource} to be concatenated * @param source4 - * a MaybeSource to be concatenated - * @return a Flowable that emits items emitted by the four source MaybeSources, one after the other. + * a {@code MaybeSource} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Concat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concat( - MaybeSource source1, MaybeSource source2, MaybeSource source3, MaybeSource source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T> Flowable concat( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, @NonNull MaybeSource source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return concatArray(source1, source2, source3, source4); } /** - * Concatenate the single values, in a non-overlapping fashion, of the MaybeSource sources provided by - * a Publisher sequence. + * Concatenate the single values, in a non-overlapping fashion, of the {@link MaybeSource} sources provided by + * a {@link Publisher} sequence as a {@link Flowable} sequence. *

* *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer and * expects the {@code Publisher} to honor backpressure as well. If the sources {@code Publisher} - * violates this, a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException} is signalled.
+ * violates this, a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException} is signaled. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Publisher of MaybeSource instances - * @return the new Flowable instance + * @param sources the {@code Publisher} of {@code MaybeSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable concat(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { return concat(sources, 2); } /** - * Concatenate the single values, in a non-overlapping fashion, of the MaybeSource sources provided by - * a Publisher sequence. + * Concatenate the single values, in a non-overlapping fashion, of the {@link MaybeSource} sources provided by + * a {@link Publisher} sequence as a {@link Flowable} sequence. *

* *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer and * expects the {@code Publisher} to honor backpressure as well. If the sources {@code Publisher} - * violates this, a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException} is signalled.
+ * violates this, a {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException} is signaled. *
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Publisher of MaybeSource instances - * @param prefetch the number of MaybeSources to prefetch from the Publisher - * @return the new Flowable instance + * @param sources the {@code Publisher} of {@code MaybeSource} instances + * @param prefetch the number of {@code MaybeSource}s to prefetch from the {@code Publisher} + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @return the new {@code Flowable} instance */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Flowable concat(Publisher> sources, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Flowable concat(@NonNull Publisher<@NonNull ? extends MaybeSource> sources, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapPublisher(sources, MaybeToPublisher.instance(), prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapMaybePublisher<>(sources, Functions.identity(), ErrorMode.IMMEDIATE, prefetch)); } /** - * Concatenate the single values, in a non-overlapping fashion, of the MaybeSource sources in the array. + * Concatenate the single values, in a non-overlapping fashion, of the {@link MaybeSource} sources in the array + * as a {@link Flowable} sequence. *

* *

@@ -347,28 +361,31 @@ public static Flowable concat(Publisher{@code concatArray} does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param sources the array of MaybeSource instances - * @return the new Flowable instance + * @param sources the array of {@code MaybeSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concatArray(MaybeSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T> Flowable concatArray(@NonNull MaybeSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return Flowable.empty(); } if (sources.length == 1) { - return RxJavaPlugins.onAssembly(new MaybeToFlowable((MaybeSource)sources[0])); + @SuppressWarnings("unchecked") + MaybeSource source = (MaybeSource)sources[0]; + return RxJavaPlugins.onAssembly(new MaybeToFlowable<>(source)); } - return RxJavaPlugins.onAssembly(new MaybeConcatArray(sources)); + return RxJavaPlugins.onAssembly(new MaybeConcatArray<>(sources)); } /** - * Concatenates a variable number of MaybeSource sources and delays errors from any of them - * till all terminate. + * Concatenates a variable number of {@link MaybeSource} sources and delays errors from any of them + * till all terminate as a {@link Flowable} sequence. *

* *

@@ -379,31 +396,35 @@ public static Flowable concatArray(MaybeSource... sources) { *
* @param sources the array of sources * @param the common base value type - * @return the new Flowable instance - * @throws NullPointerException if sources is null + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ - @SuppressWarnings("unchecked") @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatArrayDelayError(MaybeSource... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable concatArrayDelayError(@NonNull MaybeSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return Flowable.empty(); } else if (sources.length == 1) { - return RxJavaPlugins.onAssembly(new MaybeToFlowable((MaybeSource)sources[0])); + @SuppressWarnings("unchecked") + MaybeSource source = (MaybeSource)sources[0]; + return RxJavaPlugins.onAssembly(new MaybeToFlowable<>(source)); } - return RxJavaPlugins.onAssembly(new MaybeConcatArrayDelayError(sources)); + return RxJavaPlugins.onAssembly(new MaybeConcatArrayDelayError<>(sources)); } /** - * Concatenates a sequence of MaybeSource eagerly into a single stream of values. + * Concatenates a sequence of {@link MaybeSource} eagerly into a {@link Flowable} sequence. *

- * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source MaybeSources. The operator buffers the value emitted by these MaybeSources and then drains them + * Eager concatenation means that once an observer subscribes, this operator subscribes to all of the + * source {@code MaybeSource}s. The operator buffers the value emitted by these {@code MaybeSource}s and then drains them * in order, each one after the previous one completes. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -411,22 +432,55 @@ public static Flowable concatArrayDelayError(MaybeSource... *
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of MaybeSources that need to be eagerly concatenated - * @return the new Flowable instance with the specified concatenation behavior + * @param sources a sequence of {@code MaybeSource}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @SuppressWarnings({ "rawtypes", "unchecked" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatArrayEager(MaybeSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Flowable concatArrayEager(@NonNull MaybeSource... sources) { return Flowable.fromArray(sources).concatMapEager((Function)MaybeToPublisher.instance()); } + /** + * Concatenates a sequence of {@link MaybeSource} eagerly into a {@link Flowable} sequence. + *

+ * Eager concatenation means that once an observer subscribes, this operator subscribes to all of the + * source {@code MaybeSource}s. The operator buffers the value emitted by these {@code MaybeSource}s and then drains them + * in order, each one after the previous one completes. + *

+ * + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + @SafeVarargs + public static <@NonNull T> Flowable concatArrayEagerDelayError(@NonNull MaybeSource... sources) { + return Flowable.fromArray(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), true); + } /** - * Concatenates the Iterable sequence of MaybeSources into a single sequence by subscribing to each MaybeSource, - * one after the other, one at a time and delays any errors till the all inner MaybeSources terminate. + * Concatenates the {@link Iterable} sequence of {@link MaybeSource}s into a single sequence by subscribing to each {@code MaybeSource}, + * one after the other, one at a time and delays any errors till the all inner {@code MaybeSource}s terminate + * as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -435,22 +489,22 @@ public static Flowable concatArrayEager(MaybeSource... sourc *
* * @param the common element base type - * @param sources the Iterable sequence of MaybeSources - * @return the new Flowable with the concatenating behavior + * @param sources the {@code Iterable} sequence of {@code MaybeSource}s + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} */ - @SuppressWarnings({ "unchecked", "rawtypes" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatDelayError(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return Flowable.fromIterable(sources).concatMapDelayError((Function)MaybeToPublisher.instance()); + public static <@NonNull T> Flowable concatDelayError(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromIterable(sources).concatMapMaybeDelayError(Functions.identity()); } /** - * Concatenates the Publisher sequence of Publishers into a single sequence by subscribing to each inner Publisher, - * one after the other, one at a time and delays any errors till the all inner and the outer Publishers terminate. + * Concatenates the {@link Publisher} sequence of {@link MaybeSource}s into a single sequence by subscribing to each inner {@code MaybeSource}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code Publisher} terminate + * as a {@link Flowable} sequence. *

* *

@@ -461,25 +515,86 @@ public static Flowable concatDelayError(Iterable * * @param the common element base type - * @param sources the Publisher sequence of Publishers - * @return the new Publisher with the concatenating behavior + * @param sources the {@code Publisher} sequence of {@code MaybeSource}s + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromPublisher(sources).concatMapMaybeDelayError(Functions.identity()); + } + /** + * Concatenates the {@link Publisher} sequence of {@link MaybeSource}s into a single sequence by subscribing to each inner {@code MaybeSource}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code Publisher} terminate + * as a {@link Flowable} sequence. + *

+ * + *

+ *
Backpressure:
+ *
{@code concatDelayError} fully supports backpressure.
+ *
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the common element base type + * @param sources the {@code Publisher} sequence of {@code MaybeSource}s + * @param prefetch The number of upstream items to prefetch so that fresh items are + * ready to be mapped when a previous {@code MaybeSource} terminates. + * The operator replenishes after half of the prefetch amount has been consumed + * and turned into {@code MaybeSource}s. + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 */ - @SuppressWarnings({ "unchecked", "rawtypes" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatDelayError(Publisher> sources) { - return Flowable.fromPublisher(sources).concatMapDelayError((Function)MaybeToPublisher.instance()); + @NonNull + public static <@NonNull T> Flowable concatDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources, int prefetch) { + return Flowable.fromPublisher(sources).concatMapMaybeDelayError(Functions.identity(), true, prefetch); } /** - * Concatenates a sequence of MaybeSources eagerly into a single stream of values. + * Concatenates a sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence. *

- * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source MaybeSources. The operator buffers the values emitted by these MaybeSources and then drains them + * + *

+ * Eager concatenation means that once an observer subscribes, this operator subscribes to all of the + * source {@code MaybeSource}s. The operator buffers the values emitted by these {@code MaybeSource}s and then drains them * in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource} that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEager(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromIterable(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), false); + } + + /** + * Concatenates a sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence and + * runs a limited number of the inner sequences at once. *

- * + * + *

+ * Eager concatenation means that once an observer subscribes, this operator subscribes to all of the + * source {@code MaybeSource}s. The operator buffers the values emitted by these {@code MaybeSource}s and then drains them + * in order, each one after the previous one completes. *

*
Backpressure:
*
Backpressure is honored towards the downstream.
@@ -487,47 +602,224 @@ public static Flowable concatDelayError(PublisherThis method does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param sources a sequence of MaybeSource that need to be eagerly concatenated - * @return the new Flowable instance with the specified concatenation behavior + * @param sources a sequence of {@code MaybeSource} that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code MaybeSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code MaybeSource}s can be active at the same time + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 */ @SuppressWarnings({ "rawtypes", "unchecked" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatEager(Iterable> sources) { - return Flowable.fromIterable(sources).concatMapEager((Function)MaybeToPublisher.instance()); + @NonNull + public static <@NonNull T> Flowable concatEager(@NonNull Iterable<@NonNull ? extends MaybeSource> sources, int maxConcurrency) { + return Flowable.fromIterable(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), false, maxConcurrency, 1); } /** - * Concatenates a Publisher sequence of MaybeSources eagerly into a single stream of values. + * Concatenates a {@link Publisher} sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * emitted source Publishers as they are observed. The operator buffers the values emitted by these - * Publishers and then drains them in order, each one after the previous one completes. + * emitted source {@code MaybeSource}s as they are observed. The operator buffers the values emitted by these + * {@code MaybeSource}s and then drains them in order, each one after the previous one completes. *

* *

*
Backpressure:
- *
Backpressure is honored towards the downstream and the outer Publisher is + *
Backpressure is honored towards the downstream and the outer {@code Publisher} is * expected to support backpressure. Violating this assumption, the operator will * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Publishers that need to be eagerly concatenated - * @return the new Publisher instance with the specified concatenation behavior + * @param sources a sequence of {@code MaybeSource}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @SuppressWarnings({ "rawtypes", "unchecked" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatEager(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable concatEager(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { return Flowable.fromPublisher(sources).concatMapEager((Function)MaybeToPublisher.instance()); } /** - * Provides an API (via a cold Maybe) that bridges the reactive world with the callback-style world. + * Concatenates a {@link Publisher} sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence, + * running at most the given number of inner {@code MaybeSource}s at once. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code MaybeSource}s as they are observed. The operator buffers the values emitted by these + * {@code MaybeSource}s and then drains them in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the outer {@code Publisher} is + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code MaybeSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code MaybeSource}s can be active at the same time + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEager(@NonNull Publisher<@NonNull ? extends MaybeSource> sources, int maxConcurrency) { + return Flowable.fromPublisher(sources).concatMapEager((Function)MaybeToPublisher.instance(), maxConcurrency, 1); + } + + /** + * Concatenates a sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence, + * delaying errors until all inner {@code MaybeSource}s terminate. + *

+ * + *

+ * Eager concatenation means that once an observer subscribes, this operator subscribes to all of the + * source {@code MaybeSource}s. The operator buffers the values emitted by these {@code MaybeSource}s and then drains them + * in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource} that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromIterable(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), true); + } + + /** + * Concatenates a sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence, + * delaying errors until all inner {@code MaybeSource}s terminate and + * runs a limited number of inner {@code MaybeSource}s at once. + *

+ * + *

+ * Eager concatenation means that once an observer subscribes, this operator subscribes to all of the + * source {@code MaybeSource}s. The operator buffers the values emitted by these {@code MaybeSource}s and then drains them + * in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource} that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code MaybeSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code MaybeSource}s can be active at the same time + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends MaybeSource> sources, int maxConcurrency) { + return Flowable.fromIterable(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), true, maxConcurrency, 1); + } + + /** + * Concatenates a {@link Publisher} sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence, + * delaying errors until all the inner and the outer sequence terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code MaybeSource}s as they are observed. The operator buffers the values emitted by these + * {@code MaybeSource}s and then drains them in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the outer {@code Publisher} is + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource}s that need to be eagerly concatenated + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromPublisher(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), true); + } + + /** + * Concatenates a {@link Publisher} sequence of {@link MaybeSource}s eagerly into a {@link Flowable} sequence, + * delaying errors until all the inner and the outer sequence terminate and + * runs a limited number of the inner {@code MaybeSource}s at once. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code MaybeSource}s as they are observed. The operator buffers the values emitted by these + * {@code MaybeSource}s and then drains them in order, each one after the previous one completes. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the outer {@code Publisher} is + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code MaybeSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code MaybeSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code MaybeSource}s can be active at the same time + * @return the new {@code Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources, int maxConcurrency) { + return Flowable.fromPublisher(sources).concatMapEagerDelayError((Function)MaybeToPublisher.instance(), true, maxConcurrency, 1); + } + + /** + * Provides an API (via a cold {@code Maybe}) that bridges the reactive world with the callback-style world. + *

+ * *

* Example: *


@@ -558,67 +850,72 @@ public static  Flowable concatEager(Publisher
      *  
Scheduler:
*
{@code create} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param onSubscribe the emitter that is called when a MaybeObserver subscribes to the returned {@code Maybe} - * @return the new Maybe instance + * @param onSubscribe the emitter that is called when a {@code MaybeObserver} subscribes to the returned {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} * @see MaybeOnSubscribe * @see Cancellable */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe create(MaybeOnSubscribe onSubscribe) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - return RxJavaPlugins.onAssembly(new MaybeCreate(onSubscribe)); + public static <@NonNull T> Maybe create(@NonNull MaybeOnSubscribe onSubscribe) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + return RxJavaPlugins.onAssembly(new MaybeCreate<>(onSubscribe)); } /** - * Calls a Supplier for each individual MaybeObserver to return the actual MaybeSource source to + * Calls a {@link Supplier} for each individual {@link MaybeObserver} to return the actual {@link MaybeSource} source to * be subscribed to. + *

+ * *

*
Scheduler:
*
{@code defer} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param maybeSupplier the Supplier that is called for each individual MaybeObserver and - * returns a MaybeSource instance to subscribe to - * @return the new Maybe instance + * @param supplier the {@code Supplier} that is called for each individual {@code MaybeObserver} and + * returns a {@code MaybeSource} instance to subscribe to + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code supplier} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe defer(final Supplier> maybeSupplier) { - ObjectHelper.requireNonNull(maybeSupplier, "maybeSupplier is null"); - return RxJavaPlugins.onAssembly(new MaybeDefer(maybeSupplier)); + public static <@NonNull T> Maybe defer(@NonNull Supplier> supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new MaybeDefer<>(supplier)); } /** - * Returns a (singleton) Maybe instance that calls {@link MaybeObserver#onComplete onComplete} + * Returns a (singleton) {@code Maybe} instance that calls {@link MaybeObserver#onComplete onComplete} * immediately. *

- * + * *

*
Scheduler:
*
{@code empty} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @return the new Maybe instance + * @return the shared {@code Maybe} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Maybe empty() { + @NonNull + public static <@NonNull T> Maybe empty() { return RxJavaPlugins.onAssembly((Maybe)MaybeEmpty.INSTANCE); } /** - * Returns a Maybe that invokes a subscriber's {@link MaybeObserver#onError onError} method when the + * Returns a {@code Maybe} that invokes a subscriber's {@link MaybeObserver#onError onError} method when the * subscriber subscribes to it. *

* @@ -627,127 +924,133 @@ public static Maybe empty() { *

{@code error} does not operate by default on a particular {@link Scheduler}.
*
* - * @param exception - * the particular Throwable to pass to {@link MaybeObserver#onError onError} + * @param throwable + * the particular {@link Throwable} to pass to {@link MaybeObserver#onError onError} * @param - * the type of the item (ostensibly) emitted by the Maybe - * @return a Maybe that invokes the subscriber's {@link MaybeObserver#onError onError} method when - * the subscriber subscribes to it + * the type of the item (ostensibly) emitted by the {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code throwable} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe error(Throwable exception) { - ObjectHelper.requireNonNull(exception, "exception is null"); - return RxJavaPlugins.onAssembly(new MaybeError(exception)); + public static <@NonNull T> Maybe error(@NonNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is null"); + return RxJavaPlugins.onAssembly(new MaybeError<>(throwable)); } /** - * Returns a Maybe that invokes a {@link MaybeObserver}'s {@link MaybeObserver#onError onError} method when the - * MaybeObserver subscribes to it. + * Returns a {@code Maybe} that invokes a {@link MaybeObserver}'s {@link MaybeObserver#onError onError} method when the + * {@code MaybeObserver} subscribes to it. *

- * + * *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
* * @param supplier - * a Supplier factory to return a Throwable for each individual MaybeObserver + * a {@link Supplier} factory to return a {@link Throwable} for each individual {@code MaybeObserver} * @param - * the type of the items (ostensibly) emitted by the Maybe - * @return a Maybe that invokes the {@link MaybeObserver}'s {@link MaybeObserver#onError onError} method when - * the MaybeObserver subscribes to it + * the type of the items (ostensibly) emitted by the {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe error(Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "errorSupplier is null"); - return RxJavaPlugins.onAssembly(new MaybeErrorCallable(supplier)); + public static <@NonNull T> Maybe error(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new MaybeErrorCallable<>(supplier)); } /** - * Returns a Maybe instance that runs the given Action for each subscriber and + * Returns a {@code Maybe} instance that runs the given {@link Action} for each {@link MaybeObserver} and * emits either its exception or simply completes. + *

+ * *

*
Scheduler:
*
{@code fromAction} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@link Action} throws an exception, the respective {@link Throwable} is + *
If the {@code Action} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link MaybeObserver#onError(Throwable)}, - * except when the downstream has disposed this {@code Maybe} source. + * except when the downstream has disposed the resulting {@code Maybe} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. *
*
* @param the target type - * @param run the runnable to run for each subscriber - * @return the new Maybe instance - * @throws NullPointerException if run is null + * @param action the {@code Action} to run for each {@code MaybeObserver} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code action} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromAction(final Action run) { - ObjectHelper.requireNonNull(run, "run is null"); - return RxJavaPlugins.onAssembly(new MaybeFromAction(run)); + public static <@NonNull T> Maybe fromAction(@NonNull Action action) { + Objects.requireNonNull(action, "action is null"); + return RxJavaPlugins.onAssembly(new MaybeFromAction<>(action)); } /** - * Wraps a CompletableSource into a Maybe. - * + * Wraps a {@link CompletableSource} into a {@code Maybe}. + *

+ * *

*
Scheduler:
*
{@code fromCompletable} does not operate by default on a particular {@link Scheduler}.
*
* @param the target type - * @param completableSource the CompletableSource to convert from - * @return the new Maybe instance - * @throws NullPointerException if completable is null + * @param completableSource the {@code CompletableSource} to convert from + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code completableSource} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromCompletable(CompletableSource completableSource) { - ObjectHelper.requireNonNull(completableSource, "completableSource is null"); - return RxJavaPlugins.onAssembly(new MaybeFromCompletable(completableSource)); + public static <@NonNull T> Maybe fromCompletable(@NonNull CompletableSource completableSource) { + Objects.requireNonNull(completableSource, "completableSource is null"); + return RxJavaPlugins.onAssembly(new MaybeFromCompletable<>(completableSource)); } /** - * Wraps a SingleSource into a Maybe. - * + * Wraps a {@link SingleSource} into a {@code Maybe}. + *

+ * *

*
Scheduler:
*
{@code fromSingle} does not operate by default on a particular {@link Scheduler}.
*
* @param the target type - * @param singleSource the SingleSource to convert from - * @return the new Maybe instance - * @throws NullPointerException if single is null + * @param single the {@code SingleSource} to convert from + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code single} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromSingle(SingleSource singleSource) { - ObjectHelper.requireNonNull(singleSource, "singleSource is null"); - return RxJavaPlugins.onAssembly(new MaybeFromSingle(singleSource)); + public static <@NonNull T> Maybe fromSingle(@NonNull SingleSource single) { + Objects.requireNonNull(single, "single is null"); + return RxJavaPlugins.onAssembly(new MaybeFromSingle<>(single)); } /** - * Returns a {@link Maybe} that invokes the given {@link Callable} for each individual {@link MaybeObserver} that - * subscribes and emits the resulting non-null item via {@code onSuccess} while + * Returns a {@code Maybe} that invokes the given {@link Callable} for each individual {@link MaybeObserver} that + * subscribes and emits the resulting non-{@code null} item via {@code onSuccess} while * considering a {@code null} result from the {@code Callable} as indication for valueless completion * via {@code onComplete}. *

+ * + *

* This operator allows you to defer the execution of the given {@code Callable} until a {@code MaybeObserver} - * subscribes to the returned {@link Maybe}. In other terms, this source operator evaluates the given + * subscribes to the returned {@code Maybe}. In other terms, this source operator evaluates the given * {@code Callable} "lazily". *

* Note that the {@code null} handling of this operator differs from the similar source operators in the other - * {@link io.reactivex.rxjava3.core base reactive classes}. Those operators signal a {@code NullPointerException} if the value returned by their + * {@link io.reactivex.rxjava3.core base reactive classes}. Those operators signal a {@link NullPointerException} if the value returned by their * {@code Callable} is {@code null} while this {@code fromCallable} considers it to indicate the * returned {@code Maybe} is empty. *

@@ -764,129 +1067,197 @@ public static Maybe fromSingle(SingleSource singleSource) { *
* * @param callable - * a {@link Callable} instance whose execution should be deferred and performed for each individual - * {@code MaybeObserver} that subscribes to the returned {@link Maybe}. + * a {@code Callable} instance whose execution should be deferred and performed for each individual + * {@code MaybeObserver} that subscribes to the returned {@code Maybe}. * @param - * the type of the item emitted by the {@link Maybe}. - * @return a new Maybe instance + * the type of the item emitted by the {@code Maybe}. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code callable} is {@code null} * @see #defer(Supplier) * @see #fromSupplier(Supplier) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromCallable(@NonNull final Callable callable) { - ObjectHelper.requireNonNull(callable, "callable is null"); - return RxJavaPlugins.onAssembly(new MaybeFromCallable(callable)); + public static Maybe<@NonNull T> fromCallable(@NonNull Callable callable) { + Objects.requireNonNull(callable, "callable is null"); + return RxJavaPlugins.onAssembly(new MaybeFromCallable<>(callable)); } /** - * Converts a {@link Future} into a Maybe, treating a null result as an indication of emptiness. - *

- * + * Converts a {@link Future} into a {@code Maybe}, treating a {@code null} result as an indication of emptiness. *

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

- * Important note: This Maybe is blocking; you cannot dispose it. + * The operator calls {@link Future#get()}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Unlike 1.x, disposing the Maybe won't cancel the future. If necessary, one can use composition to achieve the + * Unlike 1.x, disposing the {@code Maybe} won't cancel the future. If necessary, one can use composition to achieve the * cancellation effect: {@code futureMaybe.doOnDispose(() -> future.cancel(true));}. *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting Maybe - * @return a Maybe that emits the item from the source {@link Future} + * the type of object that the {@code Future} returns, and also the type of item to be emitted by + * the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code future} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromFuture(Future future) { - ObjectHelper.requireNonNull(future, "future is null"); - return RxJavaPlugins.onAssembly(new MaybeFromFuture(future, 0L, null)); + public static <@NonNull T> Maybe fromFuture(@NonNull Future future) { + Objects.requireNonNull(future, "future is null"); + return RxJavaPlugins.onAssembly(new MaybeFromFuture<>(future, 0L, null)); } /** - * Converts a {@link Future} into a Maybe, with a timeout on the Future. + * Converts a {@link Future} into a {@code Maybe}, with a timeout on the {@code Future}. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into a Maybe that emits the - * return value of the {@link Future#get} method of that object, by passing the object into the {@code fromFuture} - * method. + * The operator calls {@link Future#get(long, TimeUnit)}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Unlike 1.x, disposing the Maybe won't cancel the future. If necessary, one can use composition to achieve the + * Unlike 1.x, disposing the {@code Maybe} won't cancel the future. If necessary, one can use composition to achieve the * cancellation effect: {@code futureMaybe.doOnCancel(() -> future.cancel(true));}. - *

- * Important note: This Maybe is blocking on the thread it gets subscribed on; you cannot dispose it. *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param timeout * the maximum time to wait before calling {@code get} * @param unit * the {@link TimeUnit} of the {@code timeout} argument * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting Maybe - * @return a Maybe that emits the item from the source {@link Future} + * the type of object that the {@code Future} returns, and also the type of item to be emitted by + * the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code future} or {@code unit} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromFuture(Future future, long timeout, TimeUnit unit) { - ObjectHelper.requireNonNull(future, "future is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - return RxJavaPlugins.onAssembly(new MaybeFromFuture(future, timeout, unit)); + public static <@NonNull T> Maybe fromFuture(@NonNull Future future, long timeout, @NonNull TimeUnit unit) { + Objects.requireNonNull(future, "future is null"); + Objects.requireNonNull(unit, "unit is null"); + return RxJavaPlugins.onAssembly(new MaybeFromFuture<>(future, timeout, unit)); } /** - * Returns a Maybe instance that runs the given Action for each subscriber and - * emits either its exception or simply completes. + * Wraps an {@link ObservableSource} into a {@code Maybe} and emits the very first item + * or completes if the source is empty. + *

+ * + *

+ *
Scheduler:
+ *
{@code fromObservable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the target type + * @param source the {@code ObservableSource} to convert from + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Maybe fromObservable(@NonNull ObservableSource source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new ObservableElementAtMaybe<>(source, 0L)); + } + + /** + * Wraps a {@link Publisher} into a {@code Maybe} and emits the very first item + * or completes if the source is empty. + *

+ * + *

+ *
Backpressure:
+ *
The operator consumes the given {@code Publisher} in an unbounded manner + * (requesting {@link Long#MAX_VALUE}) but cancels it after one item received.
+ *
Scheduler:
+ *
{@code fromPublisher} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the target type + * @param source the {@code Publisher} to convert from + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + public static <@NonNull T> Maybe fromPublisher(@NonNull Publisher source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new FlowableElementAtMaybePublisher<>(source, 0L)); + } + + /** + * Returns a {@code Maybe} instance that runs the given {@link Runnable} for each {@link MaybeObserver} and + * emits either its unchecked exception or simply completes. + *

+ * + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. *

*
Scheduler:
*
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is + * delivered to the downstream via {@link MaybeObserver#onError(Throwable)}, + * except when the downstream has disposed this {@code Maybe} source. + * In this latter case, the {@code Throwable} is delivered to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + *
*
* @param the target type - * @param run the runnable to run for each subscriber - * @return the new Maybe instance - * @throws NullPointerException if run is null + * @param run the {@code Runnable} to run for each {@code MaybeObserver} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code run} is {@code null} + * @see #fromAction(Action) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromRunnable(final Runnable run) { - ObjectHelper.requireNonNull(run, "run is null"); - return RxJavaPlugins.onAssembly(new MaybeFromRunnable(run)); + public static <@NonNull T> Maybe fromRunnable(@NonNull Runnable run) { + Objects.requireNonNull(run, "run is null"); + return RxJavaPlugins.onAssembly(new MaybeFromRunnable<>(run)); } /** - * Returns a {@link Maybe} that invokes the given {@link Supplier} for each individual {@link MaybeObserver} that - * subscribes and emits the resulting non-null item via {@code onSuccess} while + * Returns a {@code Maybe} that invokes the given {@link Supplier} for each individual {@link MaybeObserver} that + * subscribes and emits the resulting non-{@code null} item via {@code onSuccess} while * considering a {@code null} result from the {@code Supplier} as indication for valueless completion * via {@code onComplete}. *

* This operator allows you to defer the execution of the given {@code Supplier} until a {@code MaybeObserver} - * subscribes to the returned {@link Maybe}. In other terms, this source operator evaluates the given + * subscribes to the returned {@code Maybe}. In other terms, this source operator evaluates the given * {@code Supplier} "lazily". *

- * + * *

* Note that the {@code null} handling of this operator differs from the similar source operators in the other - * {@link io.reactivex.rxjava3.core base reactive classes}. Those operators signal a {@code NullPointerException} if the value returned by their + * {@link io.reactivex.rxjava3.core base reactive classes}. Those operators signal a {@link NullPointerException} if the value returned by their * {@code Supplier} is {@code null} while this {@code fromSupplier} considers it to indicate the * returned {@code Maybe} is empty. *

@@ -903,11 +1274,12 @@ public static Maybe fromRunnable(final Runnable run) { *
* * @param supplier - * a {@link Supplier} instance whose execution should be deferred and performed for each individual - * {@code MaybeObserver} that subscribes to the returned {@link Maybe}. + * a {@code Supplier} instance whose execution should be deferred and performed for each individual + * {@code MaybeObserver} that subscribes to the returned {@code Maybe}. * @param - * the type of the item emitted by the {@link Maybe}. - * @return a new Maybe instance + * the type of the item emitted by the {@code Maybe}. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see #defer(Supplier) * @see #fromCallable(Callable) * @since 3.0.0 @@ -915,9 +1287,9 @@ public static Maybe fromRunnable(final Runnable run) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe fromSupplier(@NonNull final Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new MaybeFromSupplier(supplier)); + public static Maybe<@NonNull T> fromSupplier(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new MaybeFromSupplier<>(supplier)); } /** @@ -936,33 +1308,36 @@ public static Maybe fromSupplier(@NonNull final Supplier sup * the item to emit * @param * the type of that item - * @return a {@code Maybe} that emits {@code item} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Just */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe just(T item) { - ObjectHelper.requireNonNull(item, "item is null"); - return RxJavaPlugins.onAssembly(new MaybeJust(item)); + public static <@NonNull T> Maybe just(T item) { + Objects.requireNonNull(item, "item is null"); + return RxJavaPlugins.onAssembly(new MaybeJust<>(item)); } /** - * Merges an Iterable sequence of MaybeSource instances into a single Flowable sequence, - * running all MaybeSources at once. + * Merges an {@link Iterable} sequence of {@link MaybeSource} instances into a single {@link Flowable} sequence, + * running all {@code MaybeSource}s at once. + *

+ * *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -970,33 +1345,37 @@ public static Maybe just(T item) { *
*
* @param the common and resulting value type - * @param sources the Iterable sequence of MaybeSource sources - * @return the new Flowable instance + * @param sources the {@code Iterable} sequence of {@code MaybeSource} sources + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeDelayError(Iterable) */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Iterable> sources) { - return merge(Flowable.fromIterable(sources)); + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromIterable(sources).flatMapMaybe(Functions.identity(), false, Integer.MAX_VALUE); } /** - * Merges a Flowable sequence of MaybeSource instances into a single Flowable sequence, - * running all MaybeSources at once. + * Merges a {@link Publisher} sequence of {@link MaybeSource} instances into a single {@link Flowable} sequence, + * running all {@code MaybeSource}s at once. + *

+ * *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -1004,33 +1383,37 @@ public static Flowable merge(Iterable> *
*
* @param the common and resulting value type - * @param sources the Flowable sequence of MaybeSource sources - * @return the new Flowable instance + * @param sources the {@code Flowable} sequence of {@code MaybeSource} sources + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeDelayError(Publisher) */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable merge(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { return merge(sources, Integer.MAX_VALUE); } /** - * Merges a Flowable sequence of MaybeSource instances into a single Flowable sequence, - * running at most maxConcurrency MaybeSources at once. + * Merges a {@link Publisher} sequence of {@link MaybeSource} instances into a single {@link Flowable} sequence, + * running at most maxConcurrency {@code MaybeSource}s at once. + *

+ * *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher, int)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -1038,33 +1421,34 @@ public static Flowable merge(Publisher *
*
* @param the common and resulting value type - * @param sources the Flowable sequence of MaybeSource sources - * @param maxConcurrency the maximum number of concurrently running MaybeSources - * @return the new Flowable instance + * @param sources the {@code Flowable} sequence of {@code MaybeSource} sources + * @param maxConcurrency the maximum number of concurrently running {@code MaybeSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see #mergeDelayError(Publisher, int) */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Flowable merge(Publisher> sources, int maxConcurrency) { - ObjectHelper.requireNonNull(sources, "source is null"); + public static <@NonNull T> Flowable merge(@NonNull Publisher<@NonNull ? extends MaybeSource> sources, int maxConcurrency) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapPublisher(sources, MaybeToPublisher.instance(), false, maxConcurrency, 1)); + return RxJavaPlugins.onAssembly(new FlowableFlatMapMaybePublisher<>(sources, Functions.identity(), false, maxConcurrency)); } /** - * Flattens a {@code MaybeSource} that emits a {@code MaybeSource} into a single {@code MaybeSource} that emits the item + * Flattens a {@link MaybeSource} that emits a {@code MaybeSource} into a single {@code MaybeSource} that emits the item * emitted by the nested {@code MaybeSource}, without any transformation. *

- * + * *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
The resulting {@code Maybe} emits the outer source's or the inner {@code MaybeSource}'s {@code Throwable} as is. - * Unlike the other {@code merge()} operators, this operator won't and can't produce a {@code CompositeException} because there is + *
The resulting {@code Maybe} emits the outer source's or the inner {@code MaybeSource}'s {@link Throwable} as is. + * Unlike the other {@code merge()} operators, this operator won't and can't produce a {@link CompositeException} because there is * only one possibility for the outer or the inner {@code MaybeSource} to emit an {@code onError} signal. * Therefore, there is no need for a {@code mergeDelayError(MaybeSource>)} operator. *
@@ -1073,25 +1457,25 @@ public static Flowable merge(Publisher * @param the value type of the sources and the output * @param source * a {@code MaybeSource} that emits a {@code MaybeSource} - * @return a {@code Maybe} that emits the item that is the result of flattening the {@code MaybeSource} emitted - * by {@code source} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source} is {@code null} * @see ReactiveX operators documentation: Merge */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Maybe merge(MaybeSource> source) { - ObjectHelper.requireNonNull(source, "source is null"); + public static <@NonNull T> Maybe merge(@NonNull MaybeSource> source) { + Objects.requireNonNull(source, "source is null"); return RxJavaPlugins.onAssembly(new MaybeFlatten(source, Functions.identity())); } /** - * Flattens two MaybeSources into a single Flowable, without any transformation. + * Flattens two {@link MaybeSource}s into a single {@link Flowable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple MaybeSources so that they appear as a single Flowable, by + * You can combine items emitted by multiple {@code MaybeSource}s so that they appear as a single {@code Flowable}, by * using the {@code merge} method. *

*
Backpressure:
@@ -1099,13 +1483,13 @@ public static Maybe merge(MaybeSource> *
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(MaybeSource, MaybeSource)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -1115,10 +1499,11 @@ public static Maybe merge(MaybeSource> * * @param the common value type * @param source1 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source2 - * a MaybeSource to be merged - * @return a Flowable that emits all of the items emitted by the source MaybeSources + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(MaybeSource, MaybeSource) */ @@ -1126,21 +1511,20 @@ public static Maybe merge(MaybeSource> @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable merge( - MaybeSource source1, MaybeSource source2 + public static <@NonNull T> Flowable merge( + @NonNull MaybeSource source1, @NonNull MaybeSource source2 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return mergeArray(source1, source2); } /** - * Flattens three MaybeSources into a single Flowable, without any transformation. + * Flattens three {@link MaybeSource}s into a single {@link Flowable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple MaybeSources so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code MaybeSource}s so that they appear as a single {@code Flowable}, by using * the {@code merge} method. *

*
Backpressure:
@@ -1148,13 +1532,13 @@ public static Flowable merge( *
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(MaybeSource, MaybeSource, MaybeSource)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -1164,12 +1548,13 @@ public static Flowable merge( * * @param the common value type * @param source1 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source2 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source3 - * a MaybeSource to be merged - * @return a Flowable that emits all of the items emitted by the source MaybeSources + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(MaybeSource, MaybeSource, MaybeSource) */ @@ -1177,23 +1562,22 @@ public static Flowable merge( @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable merge( - MaybeSource source1, MaybeSource source2, - MaybeSource source3 + public static <@NonNull T> Flowable merge( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, + @NonNull MaybeSource source3 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return mergeArray(source1, source2, source3); } /** - * Flattens four MaybeSources into a single Flowable, without any transformation. + * Flattens four {@link MaybeSource}s into a single {@link Flowable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple MaybeSources so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code MaybeSource}s so that they appear as a single {@code Flowable}, by using * the {@code merge} method. *

*
Backpressure:
@@ -1201,13 +1585,13 @@ public static Flowable merge( *
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(MaybeSource, MaybeSource, MaybeSource, MaybeSource)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -1217,14 +1601,15 @@ public static Flowable merge( * * @param the common value type * @param source1 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source2 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source3 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source4 - * a MaybeSource to be merged - * @return a Flowable that emits all of the items emitted by the source MaybeSources + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(MaybeSource, MaybeSource, MaybeSource, MaybeSource) */ @@ -1232,34 +1617,35 @@ public static Flowable merge( @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable merge( - MaybeSource source1, MaybeSource source2, - MaybeSource source3, MaybeSource source4 + public static <@NonNull T> Flowable merge( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, + @NonNull MaybeSource source3, @NonNull MaybeSource source4 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return mergeArray(source1, source2, source3, source4); } /** - * Merges an array sequence of MaybeSource instances into a single Flowable sequence, - * running all MaybeSources at once. + * Merges an array of {@link MaybeSource} instances into a single {@link Flowable} sequence, + * running all {@code MaybeSource}s at once. + *

+ * *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
*
{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code MaybeSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code MaybeSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code MaybeSource}s are disposed. * If more than one {@code MaybeSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeArrayDelayError(MaybeSource...)} to merge sources and terminate only when all source {@code MaybeSource}s @@ -1267,39 +1653,42 @@ public static Flowable merge( *
*
* @param the common and resulting value type - * @param sources the array sequence of MaybeSource sources - * @return the new Flowable instance + * @param sources the array sequence of {@code MaybeSource} sources + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeArrayDelayError(MaybeSource...) */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable mergeArray(MaybeSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T> Flowable mergeArray(MaybeSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return Flowable.empty(); } if (sources.length == 1) { - return RxJavaPlugins.onAssembly(new MaybeToFlowable((MaybeSource)sources[0])); + @SuppressWarnings("unchecked") + MaybeSource source = (MaybeSource)sources[0]; + return RxJavaPlugins.onAssembly(new MaybeToFlowable<>(source)); } - return RxJavaPlugins.onAssembly(new MaybeMergeArray(sources)); + return RxJavaPlugins.onAssembly(new MaybeMergeArray<>(sources)); } /** - * Flattens an array of MaybeSources into one Flowable, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source MaybeSources without being interrupted by an error + * Flattens an array of {@link MaybeSource}s into one {@link Flowable}, in a way that allows a subscriber to receive all + * successfully emitted items from each of the source {@code MaybeSource}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged MaybeSources notify of an - * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged MaybeSources have finished emitting items. + * *

- * + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code MaybeSource}s notify of an + * error via {@link Subscriber#onError onError}, {@code mergeArrayDelayError} will refrain from propagating that + * error notification until all of the merged {@code MaybeSource}s have finished emitting items. *

- * Even if multiple merged MaybeSources send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeArrayDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -1309,35 +1698,36 @@ public static Flowable mergeArray(MaybeSource... sources) { * * @param the common element base type * @param sources - * the Iterable of MaybeSources - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * MaybeSources in the Iterable + * the array of {@code MaybeSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ - @SuppressWarnings({ "unchecked", "rawtypes" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeArrayDelayError(MaybeSource... sources) { - if (sources.length == 0) { - return Flowable.empty(); - } - return Flowable.fromArray(sources).flatMap((Function)MaybeToPublisher.instance(), true, sources.length); + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable mergeArrayDelayError(@NonNull MaybeSource... sources) { + Objects.requireNonNull(sources, "sources is null"); + return Flowable.fromArray(sources).flatMapMaybe(Functions.identity(), true, Math.max(1, sources.length)); } /** - * Flattens an Iterable of MaybeSources into one Flowable, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source MaybeSources without being interrupted by an error + * Flattens an {@link Iterable} sequence of {@link MaybeSource}s into one {@link Flowable}, in a way that allows a subscriber to receive all + * successfully emitted items from each of the source {@code MaybeSource}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged MaybeSources notify of an + * + *

+ * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code MaybeSource}s notify of an * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged MaybeSources have finished emitting items. + * error notification until all of the merged {@code MaybeSource}s have finished emitting items. *

- * + * *

- * Even if multiple merged MaybeSources send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -1347,32 +1737,32 @@ public static Flowable mergeArrayDelayError(MaybeSource... s * * @param the common element base type * @param sources - * the Iterable of MaybeSources - * @return a Flowable that emits items that are the result of flattening the items emitted by the - * MaybeSources in the Iterable + * the {@code Iterable} of {@code MaybeSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ - @SuppressWarnings({ "unchecked", "rawtypes" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Iterable> sources) { - return Flowable.fromIterable(sources).flatMap((Function)MaybeToPublisher.instance(), true); + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Iterable<@NonNull ? extends MaybeSource> sources) { + return Flowable.fromIterable(sources).flatMapMaybe(Functions.identity(), true, Integer.MAX_VALUE); } /** - * Flattens a Publisher that emits MaybeSources into one Publisher, in a way that allows a Subscriber to - * receive all successfully emitted items from all of the source MaybeSources without being interrupted by - * an error notification from one of them or even the main Publisher. + * Flattens a {@link Publisher} that emits {@link MaybeSource}s into one {@link Flowable}, in a way that allows a subscriber to + * receive all successfully emitted items from all of the source {@code MaybeSource}s without being interrupted by + * an error notification from one of them or even the main {@code Publisher}. *

- * This behaves like {@link #merge(Publisher)} except that if any of the merged MaybeSources notify of an - * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged MaybeSources and the main Publisher have finished emitting items. + * *

- * + * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code MaybeSource}s notify of an + * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that + * error notification until all of the merged {@code MaybeSource}s and the main {@code Publisher} have finished emitting items. *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed @@ -1383,31 +1773,32 @@ public static Flowable mergeDelayError(Iterable the common element base type * @param sources - * a Publisher that emits MaybeSources - * @return a Flowable that emits all of the items emitted by the Publishers emitted by the - * {@code source} Publisher + * a {@code Publisher} that emits {@code MaybeSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Publisher> sources) { + @NonNull + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { return mergeDelayError(sources, Integer.MAX_VALUE); } /** - * Flattens a Publisher that emits MaybeSources into one Publisher, in a way that allows a Subscriber to - * receive all successfully emitted items from all of the source MaybeSources without being interrupted by - * an error notification from one of them or even the main Publisher as well as limiting the total number of active MaybeSources. + * Flattens a {@link Publisher} that emits {@link MaybeSource}s into one {@link Flowable}, in a way that allows a subscriber to + * receive all successfully emitted items from all of the source {@code MaybeSource}s without being interrupted by + * an error notification from one of them or even the main {@code Publisher} as well as limiting the total number of active {@code MaybeSource}s. *

- * This behaves like {@link #merge(Publisher, int)} except that if any of the merged MaybeSources notify of an - * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged MaybeSources and the main Publisher have finished emitting items. + * *

- * + * This behaves like {@link #merge(Publisher, int)} except that if any of the merged {@code MaybeSource}s notify of an + * error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from propagating that + * error notification until all of the merged {@code MaybeSource}s and the main {@code Publisher} have finished emitting items. *

- * Even if multiple merged Publishers send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream. The outer {@code Publisher} is consumed @@ -1418,37 +1809,37 @@ public static Flowable mergeDelayError(PublisherHistory: 2.1.9 - experimental * @param the common element base type * @param sources - * a Publisher that emits MaybeSources - * @param maxConcurrency the maximum number of active inner MaybeSources to be merged at a time - * @return a Flowable that emits all of the items emitted by the Publishers emitted by the - * {@code source} Publisher + * a {@code Publisher} that emits {@code MaybeSource}s + * @param maxConcurrency the maximum number of active inner {@code MaybeSource}s to be merged at a time + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: Merge * @since 2.2 */ - @SuppressWarnings({ "unchecked", "rawtypes" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Publisher> sources, int maxConcurrency) { - ObjectHelper.requireNonNull(sources, "source is null"); + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources, int maxConcurrency) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapPublisher(sources, MaybeToPublisher.instance(), true, maxConcurrency, 1)); + return RxJavaPlugins.onAssembly(new FlowableFlatMapMaybePublisher<>(sources, Functions.identity(), true, maxConcurrency)); } /** - * Flattens two MaybeSources into one Flowable, in a way that allows a Subscriber to receive all - * successfully emitted items from each of the source MaybeSources without being interrupted by an error + * Flattens two {@link MaybeSource}s into one {@link Flowable}, in a way that allows a subscriber to receive all + * successfully emitted items from each of the source {@code MaybeSource}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(MaybeSource, MaybeSource)} except that if any of the merged MaybeSources - * notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from - * propagating that error notification until all of the merged MaybeSources have finished emitting items. + * *

- * + * This behaves like {@link #merge(MaybeSource, MaybeSource)} except that if any of the merged {@code MaybeSource}s + * notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain from + * propagating that error notification until all of the merged {@code MaybeSource}s have finished emitting items. *

- * Even if both merged MaybeSources send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if both merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -1458,37 +1849,37 @@ public static Flowable mergeDelayError(Publisher the common element base type * @param source1 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source2 - * a MaybeSource to be merged - * @return a Flowable that emits all of the items that are emitted by the two source MaybeSources + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge */ - @SuppressWarnings({ "unchecked" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(MaybeSource source1, MaybeSource source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T> Flowable mergeDelayError(@NonNull MaybeSource source1, @NonNull MaybeSource source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return mergeArrayDelayError(source1, source2); } /** - * Flattens three MaybeSource into one Flowable, in a way that allows a Subscriber to receive all - * successfully emitted items from all of the source MaybeSources without being interrupted by an error + * Flattens three {@link MaybeSource} into one {@link Flowable}, in a way that allows a subscriber to receive all + * successfully emitted items from all of the source {@code MaybeSource}s without being interrupted by an error * notification from one of them. *

+ * + *

* This behaves like {@link #merge(MaybeSource, MaybeSource, MaybeSource)} except that if any of the merged - * MaybeSources notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged MaybeSources have finished emitting + * {@code MaybeSource}s notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged {@code MaybeSource}s have finished emitting * items. *

- * - *

- * Even if multiple merged MaybeSources send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -1498,41 +1889,41 @@ public static Flowable mergeDelayError(MaybeSource source1, * * @param the common element base type * @param source1 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source2 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source3 - * a MaybeSource to be merged - * @return a Flowable that emits all of the items that are emitted by the source MaybeSources + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge */ - @SuppressWarnings({ "unchecked" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(MaybeSource source1, - MaybeSource source2, MaybeSource source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T> Flowable mergeDelayError(@NonNull MaybeSource source1, + @NonNull MaybeSource source2, @NonNull MaybeSource source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return mergeArrayDelayError(source1, source2, source3); } /** - * Flattens four MaybeSources into one Flowable, in a way that allows a Subscriber to receive all - * successfully emitted items from all of the source MaybeSources without being interrupted by an error + * Flattens four {@link MaybeSource}s into one {@link Flowable}, in a way that allows a subscriber to receive all + * successfully emitted items from all of the source {@code MaybeSource}s without being interrupted by an error * notification from one of them. *

+ * + *

* This behaves like {@link #merge(MaybeSource, MaybeSource, MaybeSource, MaybeSource)} except that if any of - * the merged MaybeSources notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} - * will refrain from propagating that error notification until all of the merged MaybeSources have finished + * the merged {@code MaybeSource}s notify of an error via {@link Subscriber#onError onError}, {@code mergeDelayError} + * will refrain from propagating that error notification until all of the merged {@code MaybeSource}s have finished * emitting items. *

- * - *

- * Even if multiple merged MaybeSources send {@code onError} notifications, {@code mergeDelayError} will only - * invoke the {@code onError} method of its Subscribers once. + * Even if multiple merged {@code MaybeSource}s send {@code onError} notifications, {@code mergeDelayError} will only + * invoke the {@code onError} method of its subscribers once. *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -1542,117 +1933,189 @@ public static Flowable mergeDelayError(MaybeSource source1, * * @param the common element base type * @param source1 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source2 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source3 - * a MaybeSource to be merged + * a {@code MaybeSource} to be merged * @param source4 - * a MaybeSource to be merged - * @return a Flowable that emits all of the items that are emitted by the source MaybeSources + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge */ - @SuppressWarnings({ "unchecked" }) @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError( - MaybeSource source1, MaybeSource source2, - MaybeSource source3, MaybeSource source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T> Flowable mergeDelayError( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, + @NonNull MaybeSource source3, @NonNull MaybeSource source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return mergeArrayDelayError(source1, source2, source3, source4); } /** - * Returns a Maybe that never sends any items or notifications to a {@link MaybeObserver}. + * Returns a {@code Maybe} that never sends any items or notifications to a {@link MaybeObserver}. *

- * + * *

- * This Maybe is useful primarily for testing purposes. + * This {@code Maybe} is useful primarily for testing purposes. *

*
Scheduler:
*
{@code never} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items (not) emitted by the Maybe - * @return a Maybe that never emits any items or sends any notifications to a {@link MaybeObserver} + * the type of items (not) emitted by the {@code Maybe} + * @return the shared {@code Maybe} instance * @see ReactiveX operators documentation: Never */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Maybe never() { + @NonNull + public static <@NonNull T> Maybe never() { return RxJavaPlugins.onAssembly((Maybe)MaybeNever.INSTANCE); } /** - * Returns a Single that emits a Boolean value that indicates whether two MaybeSource sequences are the - * same by comparing the items emitted by each MaybeSource pairwise. + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link MaybeSource} sequences are the + * same by comparing the items emitted by each {@code MaybeSource} pairwise. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first MaybeSource to compare + * the first {@code MaybeSource} to compare * @param source2 - * the second MaybeSource to compare + * the second {@code MaybeSource} to compare * @param - * the type of items emitted by each MaybeSource - * @return a Single that emits a Boolean value that indicates whether the two sequences are the same + * the type of items emitted by each {@code MaybeSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(MaybeSource source1, MaybeSource source2) { + @NonNull + public static <@NonNull T> Single sequenceEqual(@NonNull MaybeSource source1, @NonNull MaybeSource source2) { return sequenceEqual(source1, source2, ObjectHelper.equalsPredicate()); } /** - * Returns a Single that emits a Boolean value that indicates whether two MaybeSources are the - * same by comparing the items emitted by each MaybeSource pairwise based on the results of a specified + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link MaybeSource}s are the + * same by comparing the items emitted by each {@code MaybeSource} pairwise based on the results of a specified * equality function. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first MaybeSource to compare + * the first {@code MaybeSource} to compare * @param source2 - * the second MaybeSource to compare + * the second {@code MaybeSource} to compare * @param isEqual - * a function used to compare items emitted by each MaybeSource + * a function used to compare items emitted by each {@code MaybeSource} * @param - * the type of items emitted by each MaybeSource - * @return a Single that emits a Boolean value that indicates whether the two MaybeSource sequences - * are the same according to the specified function + * the type of items emitted by each {@code MaybeSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code isEqual} is {@code null} * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(MaybeSource source1, MaybeSource source2, - BiPredicate isEqual) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(isEqual, "isEqual is null"); - return RxJavaPlugins.onAssembly(new MaybeEqualSingle(source1, source2, isEqual)); + public static <@NonNull T> Single sequenceEqual(@NonNull MaybeSource source1, @NonNull MaybeSource source2, + @NonNull BiPredicate isEqual) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(isEqual, "isEqual is null"); + return RxJavaPlugins.onAssembly(new MaybeEqualSingle<>(source1, source2, isEqual)); + } + + /** + * Switches between {@link MaybeSource}s emitted by the source {@link Publisher} whenever + * a new {@code MaybeSource} is emitted, disposing the previously running {@code MaybeSource}, + * exposing the success items as a {@link Flowable} sequence. + *

+ * + *

+ *
Backpressure:
+ *
The {@code sources} {@code Publisher} is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE}). + * The returned {@code Flowable} respects the backpressure from the downstream.
+ *
Scheduler:
+ *
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
The returned sequence fails with the first error signaled by the {@code sources} {@code Publisher} + * or the currently running {@code MaybeSource}, disposing the rest. Late errors are + * forwarded to the global error handler via {@link RxJavaPlugins#onError(Throwable)}.
+ *
+ * @param the element type of the {@code MaybeSource}s + * @param sources the {@code Publisher} sequence of inner {@code MaybeSource}s to switch between + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + * @see #switchOnNextDelayError(Publisher) + * @see ReactiveX operators documentation: Switch + */ + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable switchOnNext(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapMaybePublisher<>(sources, Functions.identity(), false)); + } + + /** + * Switches between {@link MaybeSource}s emitted by the source {@link Publisher} whenever + * a new {@code MaybeSource} is emitted, disposing the previously running {@code MaybeSource}, + * exposing the success items as a {@link Flowable} sequence and delaying all errors from + * all of them until all terminate. + *

+ * + *

+ *
Backpressure:
+ *
The {@code sources} {@code Publisher} is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE}). + * The returned {@code Flowable} respects the backpressure from the downstream.
+ *
Scheduler:
+ *
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
The returned {@code Flowable} collects all errors emitted by either the {@code sources} + * {@code Publisher} or any inner {@code MaybeSource} and emits them as a {@link CompositeException} + * when all sources terminate. If only one source ever failed, its error is emitted as-is at the end.
+ *
+ * @param the element type of the {@code MaybeSource}s + * @param sources the {@code Publisher} sequence of inner {@code MaybeSource}s to switch between + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + * @see #switchOnNext(Publisher) + * @see ReactiveX operators documentation: Switch + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable switchOnNextDelayError(@NonNull Publisher<@NonNull ? extends MaybeSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapMaybePublisher<>(sources, Functions.identity(), true)); } /** - * Returns a Maybe that emits {@code 0L} after a specified delay. + * Returns a {@code Maybe} that emits {@code 0L} after a specified delay. *

- * + * *

*
Scheduler:
*
{@code timer} operates by default on the {@code computation} {@link Scheduler}.
@@ -1662,22 +2125,24 @@ public static Single sequenceEqual(MaybeSource source1 * the initial delay before emitting a single {@code 0L} * @param unit * time units to use for {@code delay} - * @return a Maybe that emits {@code 0L} after a specified delay + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Timer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Maybe timer(long delay, TimeUnit unit) { + @NonNull + public static Maybe timer(long delay, @NonNull TimeUnit unit) { return timer(delay, unit, Schedulers.computation()); } /** - * Returns a Maybe that emits {@code 0L} after a specified delay on a specified Scheduler. + * Returns a {@code Maybe} that emits {@code 0L} after a specified delay on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param delay @@ -1685,145 +2150,156 @@ public static Maybe timer(long delay, TimeUnit unit) { * @param unit * time units to use for {@code delay} * @param scheduler - * the {@link Scheduler} to use for scheduling the item - * @return a Maybe that emits {@code 0L} after a specified delay, on a specified Scheduler + * the {@code Scheduler} to use for scheduling the item + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timer */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Maybe timer(long delay, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public static Maybe timer(long delay, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new MaybeTimer(Math.max(0L, delay), unit, scheduler)); } /** - * Advanced use only: creates a Maybe instance without - * any safeguards by using a callback that is called with a MaybeObserver. + * Advanced use only: creates a {@code Maybe} instance without + * any safeguards by using a callback that is called with a {@link MaybeObserver}. + *

+ * *

*
Scheduler:
*
{@code unsafeCreate} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param onSubscribe the function that is called with the subscribing MaybeObserver - * @return the new Maybe instance + * @param onSubscribe the function that is called with the subscribing {@code MaybeObserver} + * @return the new {@code Maybe} instance + * @throws IllegalArgumentException if {@code onSubscribe} is a {@code Maybe} + * @throws NullPointerException if {@code onSubscribe} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe unsafeCreate(MaybeSource onSubscribe) { + public static <@NonNull T> Maybe unsafeCreate(@NonNull MaybeSource onSubscribe) { if (onSubscribe instanceof Maybe) { throw new IllegalArgumentException("unsafeCreate(Maybe) should be upgraded"); } - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - return RxJavaPlugins.onAssembly(new MaybeUnsafeCreate(onSubscribe)); + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + return RxJavaPlugins.onAssembly(new MaybeUnsafeCreate<>(onSubscribe)); } /** - * Constructs a Maybe that creates a dependent resource object which is disposed of when the - * upstream terminates or the downstream calls dispose(). + * Constructs a {@code Maybe} that creates a dependent resource object which is disposed of when the + * generated {@link MaybeSource} terminates or the downstream calls dispose(). *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the generated MaybeSource + * @param the element type of the generated {@code MaybeSource} * @param the type of the resource associated with the output sequence * @param resourceSupplier - * the factory function to create a resource object that depends on the Maybe + * the factory function to create a resource object that depends on the {@code Maybe} * @param sourceSupplier - * the factory function to create a MaybeSource - * @param resourceDisposer + * the factory function to create a {@code MaybeSource} + * @param resourceCleanup * the function that will dispose of the resource - * @return the Maybe whose lifetime controls the lifetime of the dependent resource object + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} or {@code resourceCleanup} is {@code null} * @see ReactiveX operators documentation: Using */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe using(Supplier resourceSupplier, - Function> sourceSupplier, - Consumer resourceDisposer) { - return using(resourceSupplier, sourceSupplier, resourceDisposer, true); + @NonNull + public static <@NonNull T, @NonNull D> Maybe using(@NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup) { + return using(resourceSupplier, sourceSupplier, resourceCleanup, true); } /** - * Constructs a Maybe that creates a dependent resource object which is disposed of just before - * termination if you have set {@code disposeEagerly} to {@code true} and a downstream dispose() does not occur - * before termination. Otherwise resource disposal will occur on call to dispose(). Eager disposal is - * particularly appropriate for a synchronous Maybe that reuses resources. {@code disposeAction} will - * only be called once per subscription. + * Constructs a {@code Maybe} that creates a dependent resource object which is disposed first ({code eager == true}) + * when the generated {@link MaybeSource} terminates or the downstream disposes; or after ({code eager == false}). *

- * + * + *

+ * Eager disposal is particularly appropriate for a synchronous {@code Maybe} that reuses resources. {@code disposeAction} will + * only be called once per subscription. *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the generated MaybeSource + * @param the element type of the generated {@code MaybeSource} * @param the type of the resource associated with the output sequence * @param resourceSupplier - * the factory function to create a resource object that depends on the Maybe + * the factory function to create a resource object that depends on the {@code Maybe} * @param sourceSupplier - * the factory function to create a MaybeSource - * @param resourceDisposer + * the factory function to create a {@code MaybeSource} + * @param resourceCleanup * the function that will dispose of the resource * @param eager * If {@code true} then resource disposal will happen either on a {@code dispose()} call before the upstream is disposed * or just before the emission of a terminal event ({@code onSuccess}, {@code onComplete} or {@code onError}). * If {@code false} the resource disposal will happen either on a {@code dispose()} call after the upstream is disposed * or just after the emission of a terminal event ({@code onSuccess}, {@code onComplete} or {@code onError}). - * @return the Maybe whose lifetime controls the lifetime of the dependent resource object + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} or {@code resourceCleanup} is {@code null} * @see ReactiveX operators documentation: Using */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe using(Supplier resourceSupplier, - Function> sourceSupplier, - Consumer resourceDisposer, boolean eager) { - ObjectHelper.requireNonNull(resourceSupplier, "resourceSupplier is null"); - ObjectHelper.requireNonNull(sourceSupplier, "sourceSupplier is null"); - ObjectHelper.requireNonNull(resourceDisposer, "disposer is null"); - return RxJavaPlugins.onAssembly(new MaybeUsing(resourceSupplier, sourceSupplier, resourceDisposer, eager)); + public static <@NonNull T, @NonNull D> Maybe using(@NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup, boolean eager) { + Objects.requireNonNull(resourceSupplier, "resourceSupplier is null"); + Objects.requireNonNull(sourceSupplier, "sourceSupplier is null"); + Objects.requireNonNull(resourceCleanup, "resourceCleanup is null"); + return RxJavaPlugins.onAssembly(new MaybeUsing(resourceSupplier, sourceSupplier, resourceCleanup, eager)); } /** - * Wraps a MaybeSource instance into a new Maybe instance if not already a Maybe + * Wraps a {@link MaybeSource} instance into a new {@code Maybe} instance if not already a {@code Maybe} * instance. + *

+ * *

*
Scheduler:
*
{@code wrap} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type * @param source the source to wrap - * @return the Maybe wrapper or the source cast to Maybe (if possible) + * @return the new wrapped or cast {@code Maybe} instance + * @throws NullPointerException if {@code source} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe wrap(MaybeSource source) { + public static <@NonNull T> Maybe wrap(@NonNull MaybeSource source) { if (source instanceof Maybe) { return RxJavaPlugins.onAssembly((Maybe)source); } - ObjectHelper.requireNonNull(source, "onSubscribe is null"); - return RxJavaPlugins.onAssembly(new MaybeUnsafeCreate(source)); + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new MaybeUnsafeCreate<>(source)); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an Iterable of other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an {@link Iterable} of other {@link MaybeSource}s. *

* *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -1833,29 +2309,30 @@ public static Maybe wrap(MaybeSource source) { * @param the common value type * @param the zipped result type * @param sources - * an Iterable of source MaybeSources + * an {@code Iterable} of source {@code MaybeSource}s * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code zipper} or {@code sources} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip(Iterable> sources, Function zipper) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new MaybeZipIterable(sources, zipper)); + public static <@NonNull T, @NonNull R> Maybe zip(@NonNull Iterable<@NonNull ? extends MaybeSource> sources, @NonNull Function zipper) { + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new MaybeZipIterable<>(sources, zipper)); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -1866,34 +2343,35 @@ public static Maybe zip(Iterable> s * @param the value type of the second source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results - * in an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results + * in an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, - BiFunction zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, + @NonNull BiFunction zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * three items emitted, in sequence, by three other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * three items emitted, in sequence, by three other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -1905,37 +2383,38 @@ public static Maybe zip( * @param the value type of the third source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - Function3 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull Function3 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * four items emitted, in sequence, by four other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * four items emitted, in sequence, by four other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -1948,41 +2427,43 @@ public static Maybe zip( * @param the value type of the fourth source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param source4 - * a fourth source MaybeSource + * a fourth source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - MaybeSource source4, - Function4 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull MaybeSource source4, + @NonNull Function4 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * five items emitted, in sequence, by five other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * five items emitted, in sequence, by five other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -1996,44 +2477,46 @@ public static Maybe zip( * @param the value type of the fifth source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param source4 - * a fourth source MaybeSource + * a fourth source {@code MaybeSource} * @param source5 - * a fifth source MaybeSource + * a fifth source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - MaybeSource source4, MaybeSource source5, - Function5 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull MaybeSource source4, @NonNull MaybeSource source5, + @NonNull Function5 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * six items emitted, in sequence, by six other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * six items emitted, in sequence, by six other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -2048,47 +2531,49 @@ public static Maybe zip( * @param the value type of the sixth source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param source4 - * a fourth source MaybeSource + * a fourth source {@code MaybeSource} * @param source5 - * a fifth source MaybeSource + * a fifth source {@code MaybeSource} * @param source6 - * a sixth source MaybeSource + * a sixth source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - MaybeSource source4, MaybeSource source5, MaybeSource source6, - Function6 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull MaybeSource source4, @NonNull MaybeSource source5, @NonNull MaybeSource source6, + @NonNull Function6 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * seven items emitted, in sequence, by seven other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * seven items emitted, in sequence, by seven other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -2104,51 +2589,54 @@ public static Maybe zip( * @param the value type of the seventh source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param source4 - * a fourth source MaybeSource + * a fourth source {@code MaybeSource} * @param source5 - * a fifth source MaybeSource + * a fifth source {@code MaybeSource} * @param source6 - * a sixth source MaybeSource + * a sixth source {@code MaybeSource} * @param source7 - * a seventh source MaybeSource + * a seventh source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7} or {@code zipper} is {@code null} + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - MaybeSource source4, MaybeSource source5, MaybeSource source6, - MaybeSource source7, - Function7 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull MaybeSource source4, @NonNull MaybeSource source5, @NonNull MaybeSource source6, + @NonNull MaybeSource source7, + @NonNull Function7 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6, source7); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * eight items emitted, in sequence, by eight other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * eight items emitted, in sequence, by eight other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -2165,54 +2653,57 @@ public static Maybe zip( * @param the value type of the eighth source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param source4 - * a fourth source MaybeSource + * a fourth source {@code MaybeSource} * @param source5 - * a fifth source MaybeSource + * a fifth source {@code MaybeSource} * @param source6 - * a sixth source MaybeSource + * a sixth source {@code MaybeSource} * @param source7 - * a seventh source MaybeSource + * a seventh source {@code MaybeSource} * @param source8 - * an eighth source MaybeSource + * an eighth source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting Maybe - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - MaybeSource source4, MaybeSource source5, MaybeSource source6, - MaybeSource source7, MaybeSource source8, - Function8 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull MaybeSource source4, @NonNull MaybeSource source5, @NonNull MaybeSource source6, + @NonNull MaybeSource source7, @NonNull MaybeSource source8, + @NonNull Function8 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6, source7, source8); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * nine items emitted, in sequence, by nine other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * nine items emitted, in sequence, by nine other {@link MaybeSource}s. *

* *

- * This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -2229,62 +2720,65 @@ public static Maybe zip( * @param the value type of the ninth source * @param the zipped result type * @param source1 - * the first source MaybeSource + * the first source {@code MaybeSource} * @param source2 - * a second source MaybeSource + * a second source {@code MaybeSource} * @param source3 - * a third source MaybeSource + * a third source {@code MaybeSource} * @param source4 - * a fourth source MaybeSource + * a fourth source {@code MaybeSource} * @param source5 - * a fifth source MaybeSource + * a fifth source {@code MaybeSource} * @param source6 - * a sixth source MaybeSource + * a sixth source {@code MaybeSource} * @param source7 - * a seventh source MaybeSource + * a seventh source {@code MaybeSource} * @param source8 - * an eighth source MaybeSource + * an eighth source {@code MaybeSource} * @param source9 - * a ninth source MaybeSource + * a ninth source {@code MaybeSource} * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting MaybeSource - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8}, {@code source9} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zip( - MaybeSource source1, MaybeSource source2, MaybeSource source3, - MaybeSource source4, MaybeSource source5, MaybeSource source6, - MaybeSource source7, MaybeSource source8, MaybeSource source9, - Function9 zipper) { - - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); - ObjectHelper.requireNonNull(source9, "source9 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> Maybe zip( + @NonNull MaybeSource source1, @NonNull MaybeSource source2, @NonNull MaybeSource source3, + @NonNull MaybeSource source4, @NonNull MaybeSource source5, @NonNull MaybeSource source6, + @NonNull MaybeSource source7, @NonNull MaybeSource source8, @NonNull MaybeSource source9, + @NonNull Function9 zipper) { + + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6, source7, source8, source9); } /** - * Returns a Maybe that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an array of other MaybeSources. + * Returns a {@code Maybe} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an array of other {@link MaybeSource}s. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * - *

This operator terminates eagerly if any of the source MaybeSources signal an onError or onComplete. This + * + *

This operator terminates eagerly if any of the source {@code MaybeSource}s signal an {@code onError} or {@code onComplete}. This * also means it is possible some sources may not get subscribed to at all. *

*
Scheduler:
@@ -2294,24 +2788,26 @@ public static Maybe zip( * @param the common element type * @param the result type * @param sources - * an array of source MaybeSources + * an array of source {@code MaybeSource}s * @param zipper - * a function that, when applied to an item emitted by each of the source MaybeSources, results in - * an item that will be emitted by the resulting MaybeSource - * @return a Maybe that emits the zipped results + * a function that, when applied to an item emitted by each of the source {@code MaybeSource}s, results in + * an item that will be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Maybe zipArray(Function zipper, - MaybeSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T, @NonNull R> Maybe zipArray(@NonNull Function zipper, + @NonNull MaybeSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } - ObjectHelper.requireNonNull(zipper, "zipper is null"); - return RxJavaPlugins.onAssembly(new MaybeZipArray(sources, zipper)); + Objects.requireNonNull(zipper, "zipper is null"); + return RxJavaPlugins.onAssembly(new MaybeZipArray<>(sources, zipper)); } // ------------------------------------------------------------------ @@ -2319,33 +2815,34 @@ public static Maybe zipArray(Function z // ------------------------------------------------------------------ /** - * Mirrors the MaybeSource (current or provided) that first signals an event. + * Mirrors the {@link MaybeSource} (current or provided) that first signals an event. *

- * + * *

*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * a MaybeSource competing to react first. A subscription to this provided source will occur after + * a {@code MaybeSource} competing to react first. A subscription to this provided source will occur after * subscribing to the current source. - * @return a Maybe that emits the same sequence as whichever of the source MaybeSources first - * signalled + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Amb */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe ambWith(MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Maybe ambWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); return ambArray(this, other); } /** - * Waits in a blocking fashion until the current Maybe signals a success value (which is returned), - * null if completed or an exception (which is propagated). + * Waits in a blocking fashion until the current {@code Maybe} signals a success value (which is returned), + * {@code null} if completed or an exception (which is propagated). + *

+ * *

*
Scheduler:
*
{@code blockingGet} does not operate by default on a particular {@link Scheduler}.
@@ -2358,15 +2855,18 @@ public final Maybe ambWith(MaybeSource other) { */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @Nullable public final T blockingGet() { - BlockingMultiObserver observer = new BlockingMultiObserver(); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); subscribe(observer); return observer.blockingGet(); } /** - * Waits in a blocking fashion until the current Maybe signals a success value (which is returned), + * Waits in a blocking fashion until the current {@code Maybe} signals a success value (which is returned), * defaultValue if completed or an exception (which is propagated). + *

+ * *

*
Scheduler:
*
{@code blockingGet} does not operate by default on a particular {@link Scheduler}.
@@ -2375,26 +2875,157 @@ public final T blockingGet() { * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and * {@link Error}s are rethrown as they are.
*
- * @param defaultValue the default item to return if this Maybe is empty + * @param defaultValue the default item to return if this {@code Maybe} is empty * @return the success value + * @throws NullPointerException if {@code defaultValue} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingGet(T defaultValue) { - ObjectHelper.requireNonNull(defaultValue, "defaultValue is null"); - BlockingMultiObserver observer = new BlockingMultiObserver(); + @NonNull + public final T blockingGet(@NonNull T defaultValue) { + Objects.requireNonNull(defaultValue, "defaultValue is null"); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); subscribe(observer); return observer.blockingGet(defaultValue); } /** - * Returns a Maybe that subscribes to this Maybe lazily, caches its event + * Subscribes to the current {@code Maybe} and blocks the current thread until it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the current {@code Maybe} signals an error, + * the {@link Throwable} is routed to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, an {@link InterruptedException} is routed to the same global error handler. + *
+ *
+ * @since 3.0.0 + * @see #blockingSubscribe(Consumer) + * @see #blockingSubscribe(Consumer, Consumer) + * @see #blockingSubscribe(Consumer, Consumer, Action) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe() { + blockingSubscribe(Functions.emptyConsumer(), Functions.ERROR_CONSUMER, Functions.EMPTY_ACTION); + } + + /** + * Subscribes to the current {@code Maybe} and calls given {@code onSuccess} callback on the current thread + * when it completes normally. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either the current {@code Maybe} signals an error or {@code onSuccess} throws, + * the respective {@link Throwable} is routed to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, an {@link InterruptedException} is routed to the same global error handler. + *
+ *
+ * @param onSuccess the {@link Consumer} to call if the current {@code Maybe} succeeds + * @throws NullPointerException if {@code onSuccess} is {@code null} + * @since 3.0.0 + * @see #blockingSubscribe(Consumer, Consumer) + * @see #blockingSubscribe(Consumer, Consumer, Action) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Consumer onSuccess) { + blockingSubscribe(onSuccess, Functions.ERROR_CONSUMER, Functions.EMPTY_ACTION); + } + + /** + * Subscribes to the current {@code Maybe} and calls the appropriate callback on the current thread + * when it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either {@code onSuccess} or {@code onError} throw, the {@link Throwable} is routed to the + * global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, the {@code onError} consumer is called with an {@link InterruptedException}. + *
+ *
+ * @param onSuccess the {@link Consumer} to call if the current {@code Maybe} succeeds + * @param onError the {@code Consumer} to call if the current {@code Maybe} signals an error + * @throws NullPointerException if {@code onSuccess} or {@code onError} is {@code null} + * @since 3.0.0 + * @see #blockingSubscribe(Consumer, Consumer, Action) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Consumer onSuccess, @NonNull Consumer onError) { + blockingSubscribe(onSuccess, onError, Functions.EMPTY_ACTION); + } + + /** + * Subscribes to the current {@code Maybe} and calls the appropriate callback on the current thread + * when it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either {@code onSuccess}, {@code onError} or {@code onComplete} throw, the {@link Throwable} is routed to the + * global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, the {@code onError} consumer is called with an {@link InterruptedException}. + *
+ *
+ * @param onSuccess the {@link Consumer} to call if the current {@code Maybe} succeeds + * @param onError the {@code Consumer} to call if the current {@code Maybe} signals an error + * @param onComplete the {@link Action} to call if the current {@code Maybe} completes without a value + * @throws NullPointerException if {@code onSuccess}, {@code onError} or {@code onComplete} is {@code null} + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Consumer onSuccess, @NonNull Consumer onError, @NonNull Action onComplete) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); + subscribe(observer); + observer.blockingConsume(onSuccess, onError, onComplete); + } + + /** + * Subscribes to the current {@code Maybe} and calls the appropriate {@link MaybeObserver} method on the current thread. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
An {@code onError} signal is delivered to the {@link MaybeObserver#onError(Throwable)} method. + * If any of the {@code MaybeObserver}'s methods throw, the {@link RuntimeException} is propagated to the caller of this method. + * If the current thread is interrupted, an {@link InterruptedException} is delivered to {@code observer.onError}. + *
+ *
+ * @param observer the {@code MaybeObserver} to call methods on the current thread + * @throws NullPointerException if {@code observer} is {@code null} + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull MaybeObserver observer) { + Objects.requireNonNull(observer, "observer is null"); + BlockingDisposableMultiObserver blockingObserver = new BlockingDisposableMultiObserver<>(); + observer.onSubscribe(blockingObserver); + subscribe(blockingObserver); + blockingObserver.blockingConsume(observer); + } + + /** + * Returns a {@code Maybe} that subscribes to this {@code Maybe} lazily, caches its event * and replays it, to all the downstream subscribers. *

- * + * *

* The operator subscribes only when the first downstream subscriber subscribes and maintains - * a single subscription towards this Maybe. + * a single subscription towards this {@code Maybe}. *

* Note: You sacrifice the ability to dispose the origin when you use the {@code cache}. *

@@ -2402,19 +3033,19 @@ public final T blockingGet(T defaultValue) { *
{@code cache} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Maybe that, when first subscribed to, caches all of its items and notifications for the - * benefit of subsequent subscribers + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe cache() { - return RxJavaPlugins.onAssembly(new MaybeCache(this)); + return RxJavaPlugins.onAssembly(new MaybeCache<>(this)); } /** - * Casts the success value of the current Maybe into the target type or signals a - * ClassCastException if not compatible. + * Casts the success value of the current {@code Maybe} into the target type or signals a + * {@link ClassCastException} if not compatible. *

* *

@@ -2422,71 +3053,133 @@ public final Maybe cache() { *
{@code cast} does not operate by default on a particular {@link Scheduler}.
*
* @param the target type - * @param clazz the type token to use for casting the success result from the current Maybe - * @return the new Maybe instance + * @param clazz the type token to use for casting the success result from the current {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code clazz} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe cast(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + public final <@NonNull U> Maybe cast(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return map(Functions.castFunction(clazz)); } /** - * Transform a Maybe by applying a particular Transformer function to it. + * Transform a {@code Maybe} by applying a particular {@link MaybeTransformer} function to it. *

- * This method operates on the Maybe itself whereas {@link #lift} operates on the Maybe's MaybeObservers. + * *

- * If the operator you are creating is designed to act on the individual item emitted by a Maybe, use - * {@link #lift}. If your operator is designed to transform the source Maybe as a whole (for instance, by + * This method operates on the {@code Maybe} itself whereas {@link #lift} operates on the {@code Maybe}'s {@link MaybeObserver}s. + *

+ * If the operator you are creating is designed to act on the individual item emitted by a {@code Maybe}, use + * {@link #lift}. If your operator is designed to transform the current {@code Maybe} as a whole (for instance, by * applying a particular set of existing RxJava operators to it) use {@code compose}. *

*
Scheduler:
*
{@code compose} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the Maybe returned by the transformer function - * @param transformer the transformer function, not null - * @return a Maybe, transformed by the transformer function + * @param the value type of the {@code Maybe} returned by the transformer function + * @param transformer the transformer function, not {@code null} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code transformer} is {@code null} * @see RxJava wiki: Implementing Your Own Operators */ @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe compose(MaybeTransformer transformer) { - return wrap(((MaybeTransformer) ObjectHelper.requireNonNull(transformer, "transformer is null")).apply(this)); + @NonNull + public final <@NonNull R> Maybe compose(@NonNull MaybeTransformer transformer) { + return wrap(((MaybeTransformer) Objects.requireNonNull(transformer, "transformer is null")).apply(this)); } /** - * Returns a Maybe that is based on applying a specified function to the item emitted by the source Maybe, - * where that function returns a MaybeSource. + * Returns a {@code Maybe} that is based on applying a specified function to the item emitted by the current {@code Maybe}, + * where that function returns a {@link MaybeSource}. *

- * + * + *

+ * Note that flatMap and concatMap for {@code Maybe} is the same operation. *

*
Scheduler:
*
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
*
- *

Note that flatMap and concatMap for Maybe is the same operation. * @param the result value type * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns a MaybeSource - * @return the Maybe returned from {@code func} when applied to the item emitted by the source Maybe + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a {@code MaybeSource} + * @return the new {@code Maybe} instance + * @see ReactiveX operators documentation: FlatMap + * @throws NullPointerException if {@code mapper} is {@code null} + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> Maybe concatMap(@NonNull Function> mapper) { + return flatMap(mapper); + } + + /** + * Returns a {@link Completable} that completes based on applying a specified function to the item emitted by the + * current {@code Maybe}, where that function returns a {@code Completable}. + *

+ * + *

+ * This operator is an alias for {@link #flatMapCompletable(Function)}. + *

+ *
Scheduler:
+ *
{@code concatMapCompletable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param mapper + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a + * {@code Completable} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Completable concatMapCompletable(@NonNull Function mapper) { + return flatMapCompletable(mapper); + } + + /** + * Returns a {@code Maybe} based on applying a specified function to the item emitted by the + * current {@code Maybe}, where that function returns a {@link Single}. + * When this {@code Maybe} just completes the resulting {@code Maybe} completes as well. + *

+ * + *

+ * This operator is an alias for {@link #flatMapSingle(Function)}. + *

+ *
Scheduler:
+ *
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the result value type + * @param mapper + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a + * {@code Single} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe concatMap(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatten(this, mapper)); + public final <@NonNull R> Maybe concatMapSingle(@NonNull Function> mapper) { + return flatMapSingle(mapper); } /** - * Returns a Flowable that emits the items emitted from the current MaybeSource, then the next, one after + * Returns a {@link Flowable} that emits the items emitted from the current {@code Maybe}, then the {@code other} {@link MaybeSource}, one after * the other, without interleaving them. *

- * + * *

*
Backpressure:
*
The operator honors backpressure from downstream.
@@ -2495,22 +3188,22 @@ public final Maybe concatMap(Function * * @param other - * a MaybeSource to be concatenated after the current - * @return a Flowable that emits items emitted by the two source MaybeSources, one after the other, - * without interleaving them + * a {@code MaybeSource} to be concatenated after the current + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Concat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatWith(MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Flowable concatWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); return concat(this, other); } /** - * Returns a Single that emits a Boolean that indicates whether the source Maybe emitted a + * Returns a {@link Single} that emits a {@link Boolean} that indicates whether the current {@code Maybe} emitted a * specified item. *

* @@ -2520,119 +3213,218 @@ public final Flowable concatWith(MaybeSource other) { *

* * @param item - * the item to search for in the emissions from the source Maybe, not null - * @return a Single that emits {@code true} if the specified item is emitted by the source Maybe, - * or {@code false} if the source Maybe completes without emitting that item + * the item to search for in the emissions from the current {@code Maybe}, not {@code null} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single contains(final Object item) { - ObjectHelper.requireNonNull(item, "item is null"); - return RxJavaPlugins.onAssembly(new MaybeContains(this, item)); + public final Single contains(@NonNull Object item) { + Objects.requireNonNull(item, "item is null"); + return RxJavaPlugins.onAssembly(new MaybeContains<>(this, item)); } /** - * Returns a Single that counts the total number of items emitted (0 or 1) by the source Maybe and emits - * this count as a 64-bit Long. + * Returns a {@link Single} that counts the total number of items emitted (0 or 1) by the current {@code Maybe} and emits + * this count as a 64-bit {@link Long}. *

- * + * *

*
Scheduler:
*
{@code count} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a single item: the number of items emitted by the source Maybe as a - * 64-bit Long item + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Count */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single count() { - return RxJavaPlugins.onAssembly(new MaybeCount(this)); + return RxJavaPlugins.onAssembly(new MaybeCount<>(this)); } /** - * Returns a Single that emits the item emitted by the source Maybe or a specified default item - * if the source Maybe is empty. + * Returns a {@link Single} that emits the item emitted by the current {@code Maybe} or a specified default item + * if the current {@code Maybe} is empty. *

- * + * *

*
Scheduler:
*
{@code defaultIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the item to emit if the source Maybe emits no items - * @return a Single that emits either the specified default item if the source Maybe emits no - * item, or the item emitted by the source Maybe + * the item to emit if the current {@code Maybe} emits no items + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: DefaultIfEmpty */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single defaultIfEmpty(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); - return RxJavaPlugins.onAssembly(new MaybeToSingle(this, defaultItem)); + public final Single defaultIfEmpty(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new MaybeToSingle<>(this, defaultItem)); + } + + /** + * Maps the {@link Notification} success value of the current {@code Maybe} back into normal + * {@code onSuccess}, {@code onError} or {@code onComplete} signals. + *

+ * + *

+ * The intended use of the {@code selector} function is to perform a + * type-safe identity mapping (see example) on a source that is already of type + * {@code Notification}. The Java language doesn't allow + * limiting instance methods to a certain generic argument shape, therefore, + * a function is used to ensure the conversion remains type safe. + *

+ * Regular {@code onError} or {@code onComplete} signals from the current {@code Maybe} are passed along to the downstream. + *

+ *
Scheduler:
+ *
{@code dematerialize} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

+ * Example: + *


+     * Maybe.just(Notification.createOnNext(1))
+     * .dematerialize(notification -> notification)
+     * .test()
+     * .assertResult(1);
+     * 
+ * @param the result type + * @param selector the function called with the success item and should + * return a {@code Notification} instance. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code selector} is {@code null} + * @since 3.0.0 + * @see #materialize() + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> Maybe dematerialize(@NonNull Function> selector) { + Objects.requireNonNull(selector, "selector is null"); + return RxJavaPlugins.onAssembly(new MaybeDematerialize<>(this, selector)); } /** - * Returns a Maybe that signals the events emitted by the source Maybe shifted forward in time by a + * Returns a {@code Maybe} that signals the events emitted by the current {@code Maybe} shifted forward in time by a * specified delay. + * An error signal will not be delayed. *

- * + * *

*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit - * the {@link TimeUnit} in which {@code period} is defined - * @return the new Maybe instance + * the {@link TimeUnit} in which {@code time} is defined + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @see ReactiveX operators documentation: Delay + * @see #delay(long, TimeUnit, Scheduler, boolean) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.COMPUTATION) + @NonNull + public final Maybe delay(long time, @NonNull TimeUnit unit) { + return delay(time, unit, Schedulers.computation(), false); + } + + /** + * Returns a {@code Maybe} that signals the events emitted by the current {@code Maybe} shifted forward in time by a + * specified delay. + *

+ * + *

+ *
Scheduler:
+ *
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
+ *
+ * + * @param time the delay to shift the source by + * @param unit the {@link TimeUnit} in which {@code time} is defined + * @param delayError if {@code true}, both success and error signals are delayed. if {@code false}, only success signals are delayed. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay - * @see #delay(long, TimeUnit, Scheduler) + * @see #delay(long, TimeUnit, Scheduler, boolean) + * @since 3.0.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Maybe delay(long delay, TimeUnit unit) { - return delay(delay, unit, Schedulers.computation()); + @NonNull + public final Maybe delay(long time, @NonNull TimeUnit unit, boolean delayError) { + return delay(time, unit, Schedulers.computation(), delayError); + } + + /** + * Returns a {@code Maybe} that signals the events emitted by the current {@code Maybe} shifted forward in time by a + * specified delay. + * An error signal will not be delayed. + *

+ * + *

+ *
Scheduler:
+ *
you specify the {@link Scheduler} where the non-blocking wait and emission happens
+ *
+ * + * @param time the delay to shift the source by + * @param unit the {@link TimeUnit} in which {@code time} is defined + * @param scheduler the {@code Scheduler} to use for delaying + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @see ReactiveX operators documentation: Delay + * @see #delay(long, TimeUnit, Scheduler, boolean) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + public final Maybe delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delay(time, unit, scheduler, false); } /** - * Returns a Maybe that signals the events emitted by the source Maybe shifted forward in time by a - * specified delay running on the specified Scheduler. + * Returns a {@code Maybe} that signals the events emitted by the current {@code Maybe} shifted forward in time by a + * specified delay running on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
you specify which {@link Scheduler} this operator will use.
+ *
you specify which {@code Scheduler} this operator will use.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit - * the time unit of {@code delay} + * the {@link TimeUnit} in which {@code time} is defined * @param scheduler - * the {@link Scheduler} to use for delaying - * @return the new Maybe instance + * the {@code Scheduler} to use for delaying + * @param delayError if {@code true}, both success and error signals are delayed. if {@code false}, only success signals are delayed. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay + * @since 3.0.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe delay(long delay, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new MaybeDelay(this, Math.max(0L, delay), unit, scheduler)); + public final Maybe delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new MaybeDelay<>(this, Math.max(0L, time), unit, scheduler, delayError)); } /** - * Delays the emission of this Maybe until the given Publisher signals an item or completes. + * Delays the emission of this {@code Maybe} until the given {@link Publisher} signals an item or completes. *

- * + * *

*
Backpressure:
*
The {@code delayIndicator} is consumed in an unbounded manner but is cancelled after @@ -2643,26 +3435,25 @@ public final Maybe delay(long delay, TimeUnit unit, Scheduler scheduler) { * * @param * the subscription delay value type (ignored) - * @param - * the item delay value type (ignored) * @param delayIndicator - * the Publisher that gets subscribed to when this Maybe signals an event and that - * signal is emitted when the Publisher signals an item or completes - * @return the new Maybe instance + * the {@code Publisher} that gets subscribed to when this {@code Maybe} signals an event and that + * signal is emitted when the {@code Publisher} signals an item or completes + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code delayIndicator} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) - public final Maybe delay(Publisher delayIndicator) { - ObjectHelper.requireNonNull(delayIndicator, "delayIndicator is null"); - return RxJavaPlugins.onAssembly(new MaybeDelayOtherPublisher(this, delayIndicator)); + public final <@NonNull U> Maybe delay(@NonNull Publisher delayIndicator) { + Objects.requireNonNull(delayIndicator, "delayIndicator is null"); + return RxJavaPlugins.onAssembly(new MaybeDelayOtherPublisher<>(this, delayIndicator)); } /** - * Returns a Maybe that delays the subscription to this Maybe - * until the other Publisher emits an element or completes normally. + * Returns a {@code Maybe} that delays the subscription to this {@code Maybe} + * until the other {@link Publisher} emits an element or completes normally. *

* *

@@ -2672,23 +3463,23 @@ public final Maybe delay(Publisher delayIndicator) { *
This method does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the other Publisher, irrelevant - * @param subscriptionIndicator the other Publisher that should trigger the subscription - * to this Publisher. - * @return a Maybe that delays the subscription to this Maybe - * until the other Publisher emits an element or completes normally. + * @param the value type of the other {@code Publisher}, irrelevant + * @param subscriptionIndicator the other {@code Publisher} that should trigger the subscription + * to this {@code Publisher}. + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} + * @return the new {@code Maybe} instance */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe delaySubscription(Publisher subscriptionIndicator) { - ObjectHelper.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); - return RxJavaPlugins.onAssembly(new MaybeDelaySubscriptionOtherPublisher(this, subscriptionIndicator)); + public final <@NonNull U> Maybe delaySubscription(@NonNull Publisher subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new MaybeDelaySubscriptionOtherPublisher<>(this, subscriptionIndicator)); } /** - * Returns a Maybe that delays the subscription to the source Maybe by a given amount of time. + * Returns a {@code Maybe} that delays the subscription to the current {@code Maybe} by a given amount of time. *

* *

@@ -2696,102 +3487,111 @@ public final Maybe delaySubscription(Publisher subscriptionIndicator) *
This version of {@code delaySubscription} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the time to delay the subscription * @param unit * the time unit of {@code delay} - * @return a Maybe that delays the subscription to the source Maybe by the given amount + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay * @see #delaySubscription(long, TimeUnit, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Maybe delaySubscription(long delay, TimeUnit unit) { - return delaySubscription(delay, unit, Schedulers.computation()); + @NonNull + public final Maybe delaySubscription(long time, @NonNull TimeUnit unit) { + return delaySubscription(time, unit, Schedulers.computation()); } /** - * Returns a Maybe that delays the subscription to the source Maybe by a given amount of time, - * both waiting and subscribing on a given Scheduler. + * Returns a {@code Maybe} that delays the subscription to the current {@code Maybe} by a given amount of time, + * both waiting and subscribing on a given {@link Scheduler}. *

* *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* - * @param delay + * @param time * the time to delay the subscription * @param unit * the time unit of {@code delay} * @param scheduler - * the Scheduler on which the waiting and subscription will happen - * @return a Maybe that delays the subscription to the source Maybe by a given - * amount, waiting and subscribing on the given Scheduler + * the {@code Scheduler} on which the waiting and subscription will happen + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe delaySubscription(long delay, TimeUnit unit, Scheduler scheduler) { - return delaySubscription(Flowable.timer(delay, unit, scheduler)); + @NonNull + public final Maybe delaySubscription(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delaySubscription(Flowable.timer(time, unit, scheduler)); } /** - * Calls the specified consumer with the success item after this item has been emitted to the downstream. - *

Note that the {@code onAfterNext} action is shared between subscriptions and as such + * Calls the specified {@link Consumer} with the success item after this item has been emitted to the downstream. + *

Note that the {@code onAfterSuccess} action is shared between subscriptions and as such * should be thread-safe. + *

+ * *

*
Scheduler:
*
{@code doAfterSuccess} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.1 - experimental - * @param onAfterSuccess the Consumer that will be called after emitting an item from upstream to the downstream - * @return the new Maybe instance + * @param onAfterSuccess the {@code Consumer} that will be called after emitting an item from upstream to the downstream + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onAfterSuccess} is {@code null} * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doAfterSuccess(Consumer onAfterSuccess) { - ObjectHelper.requireNonNull(onAfterSuccess, "onAfterSuccess is null"); - return RxJavaPlugins.onAssembly(new MaybeDoAfterSuccess(this, onAfterSuccess)); + public final Maybe doAfterSuccess(@NonNull Consumer onAfterSuccess) { + Objects.requireNonNull(onAfterSuccess, "onAfterSuccess is null"); + return RxJavaPlugins.onAssembly(new MaybeDoAfterSuccess<>(this, onAfterSuccess)); } /** - * Registers an {@link Action} to be called when this Maybe invokes either + * Registers an {@link Action} to be called when this {@code Maybe} invokes either * {@link MaybeObserver#onComplete onSuccess}, * {@link MaybeObserver#onComplete onComplete} or {@link MaybeObserver#onError onError}. *

- * + * *

*
Scheduler:
*
{@code doAfterTerminate} does not operate by default on a particular {@link Scheduler}.
*
* * @param onAfterTerminate - * an {@link Action} to be invoked when the source Maybe finishes - * @return a Maybe that emits the same items as the source Maybe, then invokes the - * {@link Action} + * an {@code Action} to be invoked when the current {@code Maybe} finishes + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doAfterTerminate(Action onAfterTerminate) { - return RxJavaPlugins.onAssembly(new MaybePeek(this, + public final Maybe doAfterTerminate(@NonNull Action onAfterTerminate) { + return RxJavaPlugins.onAssembly(new MaybePeek<>(this, Functions.emptyConsumer(), // onSubscribe Functions.emptyConsumer(), // onSuccess Functions.emptyConsumer(), // onError Functions.EMPTY_ACTION, // onComplete - ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null"), + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"), Functions.EMPTY_ACTION // dispose )); } /** - * Calls the specified action after this Maybe signals onSuccess, onError or onComplete or gets disposed by + * Calls the specified action after this {@code Maybe} signals {@code onSuccess}, {@code onError} or {@code onComplete} or gets disposed by * the downstream. - *

In case of a race between a terminal event and a dispose call, the provided {@code onFinally} action + *

+ * + *

+ * In case of a race between a terminal event and a dispose call, the provided {@code onFinally} action * is executed once per subscription. *

Note that the {@code onFinally} action is shared between subscriptions and as such * should be thread-safe. @@ -2800,91 +3600,96 @@ public final Maybe doAfterTerminate(Action onAfterTerminate) { *

{@code doFinally} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.1 - experimental - * @param onFinally the action called when this Maybe terminates or gets disposed - * @return the new Maybe instance + * @param onFinally the action called when this {@code Maybe} terminates or gets disposed + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onFinally} is {@code null} * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doFinally(Action onFinally) { - ObjectHelper.requireNonNull(onFinally, "onFinally is null"); - return RxJavaPlugins.onAssembly(new MaybeDoFinally(this, onFinally)); + public final Maybe doFinally(@NonNull Action onFinally) { + Objects.requireNonNull(onFinally, "onFinally is null"); + return RxJavaPlugins.onAssembly(new MaybeDoFinally<>(this, onFinally)); } /** - * Calls the shared {@code Action} if a MaybeObserver subscribed to the current Maybe - * disposes the common Disposable it received via onSubscribe. + * Calls the shared {@link Action} if a {@link MaybeObserver} subscribed to the current {@code Maybe} + * disposes the common {@link Disposable} it received via {@code onSubscribe}. + *

+ * *

*
Scheduler:
*
{@code doOnDispose} does not operate by default on a particular {@link Scheduler}.
*
* @param onDispose the action called when the subscription is disposed - * @throws NullPointerException if onDispose is null - * @return the new Maybe instance + * @throws NullPointerException if {@code onDispose} is {@code null} + * @return the new {@code Maybe} instance */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnDispose(Action onDispose) { - return RxJavaPlugins.onAssembly(new MaybePeek(this, + public final Maybe doOnDispose(@NonNull Action onDispose) { + return RxJavaPlugins.onAssembly(new MaybePeek<>(this, Functions.emptyConsumer(), // onSubscribe Functions.emptyConsumer(), // onSuccess Functions.emptyConsumer(), // onError Functions.EMPTY_ACTION, // onComplete Functions.EMPTY_ACTION, // (onSuccess | onError | onComplete) after - ObjectHelper.requireNonNull(onDispose, "onDispose is null") + Objects.requireNonNull(onDispose, "onDispose is null") )); } /** - * Modifies the source Maybe so that it invokes an action when it calls {@code onComplete}. + * Invokes an {@link Action} just before the current {@code Maybe} calls {@code onComplete}. *

- * + * *

*
Scheduler:
*
{@code doOnComplete} does not operate by default on a particular {@link Scheduler}.
*
* * @param onComplete - * the action to invoke when the source Maybe calls {@code onComplete} - * @return the new Maybe with the side-effecting behavior applied + * the action to invoke when the current {@code Maybe} calls {@code onComplete} + * @return the new {@code Maybe} with the side-effecting behavior applied + * @throws NullPointerException if {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnComplete(Action onComplete) { - return RxJavaPlugins.onAssembly(new MaybePeek(this, + public final Maybe doOnComplete(@NonNull Action onComplete) { + return RxJavaPlugins.onAssembly(new MaybePeek<>(this, Functions.emptyConsumer(), // onSubscribe Functions.emptyConsumer(), // onSuccess Functions.emptyConsumer(), // onError - ObjectHelper.requireNonNull(onComplete, "onComplete is null"), + Objects.requireNonNull(onComplete, "onComplete is null"), Functions.EMPTY_ACTION, // (onSuccess | onError | onComplete) Functions.EMPTY_ACTION // dispose )); } /** - * Calls the shared consumer with the error sent via onError for each - * MaybeObserver that subscribes to the current Maybe. + * Calls the shared {@link Consumer} with the error sent via {@code onError} for each + * {@link MaybeObserver} that subscribes to the current {@code Maybe}. *

- * + * *

*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
*
- * @param onError the consumer called with the success value of onError - * @return the new Maybe instance + * @param onError the consumer called with the success value of {@code onError} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onError} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnError(Consumer onError) { - return RxJavaPlugins.onAssembly(new MaybePeek(this, + public final Maybe doOnError(@NonNull Consumer onError) { + return RxJavaPlugins.onAssembly(new MaybePeek<>(this, Functions.emptyConsumer(), // onSubscribe Functions.emptyConsumer(), // onSuccess - ObjectHelper.requireNonNull(onError, "onError is null"), + Objects.requireNonNull(onError, "onError is null"), Functions.EMPTY_ACTION, // onComplete Functions.EMPTY_ACTION, // (onSuccess | onError | onComplete) Functions.EMPTY_ACTION // dispose @@ -2892,44 +3697,77 @@ public final Maybe doOnError(Consumer onError) { } /** - * Calls the given onEvent callback with the (success value, null) for an onSuccess, (null, throwable) for - * an onError or (null, null) for an onComplete signal from this Maybe before delivering said + * Calls the given {@code onEvent} callback with the (success value, {@code null}) for an {@code onSuccess}, ({@code null}, throwable) for + * an {@code onError} or ({@code null}, {@code null}) for an {@code onComplete} signal from this {@code Maybe} before delivering said * signal to the downstream. *

* *

- * Exceptions thrown from the callback will override the event so the downstream receives the + * The exceptions thrown from the callback will override the event so the downstream receives the * error instead of the original signal. *

*
Scheduler:
*
{@code doOnEvent} does not operate by default on a particular {@link Scheduler}.
*
- * @param onEvent the callback to call with the terminal event tuple - * @return the new Maybe instance + * @param onEvent the callback to call with the success value or the exception, whichever is not {@code null} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onEvent} is {@code null} + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Maybe doOnEvent(@NonNull BiConsumer<@Nullable ? super T, @Nullable ? super Throwable> onEvent) { + Objects.requireNonNull(onEvent, "onEvent is null"); + return RxJavaPlugins.onAssembly(new MaybeDoOnEvent<>(this, onEvent)); + } + + /** + * Calls the appropriate {@code onXXX} method (shared between all {@link MaybeObserver}s) for the lifecycle events of + * the sequence (subscription, disposal). + *

+ * + *

+ *
Scheduler:
+ *
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param onSubscribe + * a {@link Consumer} called with the {@link Disposable} sent via {@link MaybeObserver#onSubscribe(Disposable)} + * @param onDispose + * called when the downstream disposes the {@code Disposable} via {@code dispose()} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onSubscribe} or {@code onDispose} is {@code null} + * @see ReactiveX operators documentation: Do + * @since 3.0.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnEvent(BiConsumer onEvent) { - ObjectHelper.requireNonNull(onEvent, "onEvent is null"); - return RxJavaPlugins.onAssembly(new MaybeDoOnEvent(this, onEvent)); + @NonNull + public final Maybe doOnLifecycle(@NonNull Consumer onSubscribe, @NonNull Action onDispose) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + Objects.requireNonNull(onDispose, "onDispose is null"); + return RxJavaPlugins.onAssembly(new MaybeDoOnLifecycle<>(this, onSubscribe, onDispose)); } /** - * Calls the shared consumer with the Disposable sent through the onSubscribe for each - * MaybeObserver that subscribes to the current Maybe. + * Calls the shared {@link Consumer} with the {@link Disposable} sent through the {@code onSubscribe} for each + * {@link MaybeObserver} that subscribes to the current {@code Maybe}. + *

+ * *

*
Scheduler:
*
{@code doOnSubscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param onSubscribe the consumer called with the Disposable sent via onSubscribe - * @return the new Maybe instance + * @param onSubscribe the {@code Consumer} called with the {@code Disposable} sent via {@code onSubscribe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnSubscribe(Consumer onSubscribe) { - return RxJavaPlugins.onAssembly(new MaybePeek(this, - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"), + public final Maybe doOnSubscribe(@NonNull Consumer onSubscribe) { + return RxJavaPlugins.onAssembly(new MaybePeek<>(this, + Objects.requireNonNull(onSubscribe, "onSubscribe is null"), Functions.emptyConsumer(), // onSuccess Functions.emptyConsumer(), // onError Functions.EMPTY_ACTION, // onComplete @@ -2939,10 +3777,10 @@ public final Maybe doOnSubscribe(Consumer onSubscribe) { } /** - * Returns a Maybe instance that calls the given onTerminate callback - * just before this Maybe completes normally or with an exception. + * Returns a {@code Maybe} instance that calls the given onTerminate callback + * just before this {@code Maybe} completes normally or with an exception. *

- * + * *

* This differs from {@code doAfterTerminate} in that this happens before the {@code onComplete} or * {@code onError} notification. @@ -2952,7 +3790,8 @@ public final Maybe doOnSubscribe(Consumer onSubscribe) { *

*

History: 2.2.7 - experimental * @param onTerminate the action to invoke when the consumer calls {@code onComplete} or {@code onError} - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @see #doOnTerminate(Action) * @since 3.0.0 @@ -2960,30 +3799,31 @@ public final Maybe doOnSubscribe(Consumer onSubscribe) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnTerminate(final Action onTerminate) { - ObjectHelper.requireNonNull(onTerminate, "onTerminate is null"); - return RxJavaPlugins.onAssembly(new MaybeDoOnTerminate(this, onTerminate)); + public final Maybe doOnTerminate(@NonNull Action onTerminate) { + Objects.requireNonNull(onTerminate, "onTerminate is null"); + return RxJavaPlugins.onAssembly(new MaybeDoOnTerminate<>(this, onTerminate)); } /** - * Calls the shared consumer with the success value sent via onSuccess for each - * MaybeObserver that subscribes to the current Maybe. + * Calls the shared {@link Consumer} with the success value sent via {@code onSuccess} for each + * {@link MaybeObserver} that subscribes to the current {@code Maybe}. *

- * + * *

*
Scheduler:
*
{@code doOnSuccess} does not operate by default on a particular {@link Scheduler}.
*
- * @param onSuccess the consumer called with the success value of onSuccess - * @return the new Maybe instance + * @param onSuccess the {@code Consumer} called with the success value of the upstream + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onSuccess} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe doOnSuccess(Consumer onSuccess) { - return RxJavaPlugins.onAssembly(new MaybePeek(this, + public final Maybe doOnSuccess(@NonNull Consumer onSuccess) { + return RxJavaPlugins.onAssembly(new MaybePeek<>(this, Functions.emptyConsumer(), // onSubscribe - ObjectHelper.requireNonNull(onSuccess, "onSuccess is null"), + Objects.requireNonNull(onSuccess, "onSuccess is null"), Functions.emptyConsumer(), // onError Functions.EMPTY_ACTION, // onComplete Functions.EMPTY_ACTION, // (onSuccess | onError | onComplete) @@ -2992,58 +3832,59 @@ public final Maybe doOnSuccess(Consumer onSuccess) { } /** - * Filters the success item of the Maybe via a predicate function and emitting it if the predicate - * returns true, completing otherwise. + * Filters the success item of the {@code Maybe} via a predicate function and emitting it if the predicate + * returns {@code true}, completing otherwise. *

- * + * *

*
Scheduler:
*
{@code filter} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates the item emitted by the source Maybe, returning {@code true} + * a function that evaluates the item emitted by the current {@code Maybe}, returning {@code true} * if it passes the filter - * @return a Maybe that emit the item emitted by the source Maybe that the filter - * evaluates as {@code true} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe filter(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new MaybeFilter(this, predicate)); + public final Maybe filter(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new MaybeFilter<>(this, predicate)); } /** - * Returns a Maybe that is based on applying a specified function to the item emitted by the source Maybe, - * where that function returns a MaybeSource. + * Returns a {@code Maybe} that is based on applying a specified function to the item emitted by the current {@code Maybe}, + * where that function returns a {@link MaybeSource}. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
- *

Note that flatMap and concatMap for Maybe is the same operation. + *

Note that flatMap and concatMap for {@code Maybe} is the same operation. * * @param the result value type * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns a MaybeSource - * @return the Maybe returned from {@code func} when applied to the item emitted by the source Maybe + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a {@code MaybeSource} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe flatMap(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatten(this, mapper)); + public final <@NonNull R> Maybe flatMap(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatten<>(this, mapper)); } /** - * Maps the onSuccess, onError or onComplete signals of this Maybe into MaybeSource and emits that - * MaybeSource's signals. + * Maps the {@code onSuccess}, {@code onError} or {@code onComplete} signals of the current {@code Maybe} into a {@link MaybeSource} and emits that + * {@code MaybeSource}'s signals. *

* *

@@ -3054,61 +3895,63 @@ public final Maybe flatMap(Function * the result type * @param onSuccessMapper - * a function that returns a MaybeSource to merge for the onSuccess item emitted by this Maybe + * a function that returns a {@code MaybeSource} to merge for the {@code onSuccess} item emitted by this {@code Maybe} * @param onErrorMapper - * a function that returns a MaybeSource to merge for an onError notification from this Maybe + * a function that returns a {@code MaybeSource} to merge for an {@code onError} notification from this {@code Maybe} * @param onCompleteSupplier - * a function that returns a MaybeSource to merge for an onComplete notification this Maybe - * @return the new Maybe instance + * a function that returns a {@code MaybeSource} to merge for an {@code onComplete} notification this {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code onSuccessMapper}, {@code onErrorMapper} or {@code onCompleteSupplier} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe flatMap( - Function> onSuccessMapper, - Function> onErrorMapper, - Supplier> onCompleteSupplier) { - ObjectHelper.requireNonNull(onSuccessMapper, "onSuccessMapper is null"); - ObjectHelper.requireNonNull(onErrorMapper, "onErrorMapper is null"); - ObjectHelper.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapNotification(this, onSuccessMapper, onErrorMapper, onCompleteSupplier)); + public final <@NonNull R> Maybe flatMap( + @NonNull Function> onSuccessMapper, + @NonNull Function> onErrorMapper, + @NonNull Supplier> onCompleteSupplier) { + Objects.requireNonNull(onSuccessMapper, "onSuccessMapper is null"); + Objects.requireNonNull(onErrorMapper, "onErrorMapper is null"); + Objects.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapNotification<>(this, onSuccessMapper, onErrorMapper, onCompleteSupplier)); } /** - * Returns a Maybe that emits the results of a specified function to the pair of values emitted by the - * source Maybe and a specified mapped MaybeSource. + * Returns a {@code Maybe} that emits the results of a specified function to the pair of values emitted by the + * current {@code Maybe} and a specified mapped {@link MaybeSource}. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the MaybeSource returned by the {@code mapper} function + * the type of items emitted by the {@code MaybeSource} returned by the {@code mapper} function * @param - * the type of items emitted by the resulting Maybe + * the type of items emitted by the resulting {@code Maybe} * @param mapper - * a function that returns a MaybeSource for the item emitted by the source Maybe - * @param resultSelector - * a function that combines one item emitted by each of the source and collection MaybeSource and - * returns an item to be emitted by the resulting MaybeSource - * @return the new Maybe instance + * a function that returns a {@code MaybeSource} for the item emitted by the current {@code Maybe} + * @param combiner + * a function that combines one item emitted by each of the source and collection {@code MaybeSource} and + * returns an item to be emitted by the resulting {@code MaybeSource} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe flatMap(Function> mapper, - BiFunction resultSelector) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapBiSelector(this, mapper, resultSelector)); + public final <@NonNull U, @NonNull R> Maybe flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapBiSelector<>(this, mapper, combiner)); } /** - * Maps the success value of the upstream {@link Maybe} into an {@link Iterable} and emits its items as a + * Maps the success value of the current {@code Maybe} into an {@link Iterable} and emits its items as a * {@link Flowable} sequence. *

* @@ -3120,24 +3963,26 @@ public final Maybe flatMap(Function * * @param - * the type of item emitted by the resulting Iterable + * the type of item emitted by the inner {@code Iterable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Maybe - * @return the new Flowable instance + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Maybe} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap + * @see #flattenStreamAsFlowable(Function) */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flattenAsFlowable(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapIterableFlowable(this, mapper)); + public final <@NonNull U> Flowable flattenAsFlowable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapIterableFlowable<>(this, mapper)); } /** - * Maps the success value of the upstream {@link Maybe} into an {@link Iterable} and emits its items as an + * Maps the success value of the current {@code Maybe} into an {@link Iterable} and emits its items as an * {@link Observable} sequence. *

* @@ -3147,26 +3992,27 @@ public final Flowable flattenAsFlowable(final Function * * @param - * the type of item emitted by the resulting Iterable + * the type of item emitted by the resulting {@code Iterable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Maybe - * @return the new Observable instance + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Maybe} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flattenAsObservable(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapIterableObservable(this, mapper)); + public final <@NonNull U> Observable flattenAsObservable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapIterableObservable<>(this, mapper)); } /** - * Returns an Observable that is based on applying a specified function to the item emitted by the source Maybe, - * where that function returns an ObservableSource. + * Returns an {@link Observable} that is based on applying a specified function to the item emitted by the current {@code Maybe}, + * where that function returns an {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
{@code flatMapObservable} does not operate by default on a particular {@link Scheduler}.
@@ -3174,126 +4020,104 @@ public final Observable flattenAsObservable(final Function the result value type * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns an ObservableSource - * @return the Observable returned from {@code func} when applied to the item emitted by the source Maybe + * a function that, when applied to the item emitted by the current {@code Maybe}, returns an {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapObservable(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapObservable(this, mapper)); + public final <@NonNull R> Observable flatMapObservable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapObservable<>(this, mapper)); } /** - * Returns a Flowable that emits items based on applying a specified function to the item emitted by the - * source Maybe, where that function returns a Publisher. + * Returns a {@link Flowable} that emits items based on applying a specified function to the item emitted by the + * current {@code Maybe}, where that function returns a {@link Publisher}. *

- * + * *

*
Backpressure:
- *
The returned Flowable honors the downstream backpressure.
+ *
The returned {@code Flowable} honors the downstream backpressure.
*
Scheduler:
*
{@code flatMapPublisher} does not operate by default on a particular {@link Scheduler}.
*
* * @param the result value type * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns a - * Flowable - * @return the Flowable returned from {@code func} when applied to the item emitted by the source Maybe + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a + * {@code Flowable} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapPublisher(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapPublisher(this, mapper)); + public final <@NonNull R> Flowable flatMapPublisher(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapPublisher<>(this, mapper)); } /** - * Returns a {@link Single} based on applying a specified function to the item emitted by the - * source {@link Maybe}, where that function returns a {@link Single}. - * When this Maybe completes a {@link NoSuchElementException} will be thrown. + * Returns a {@code Maybe} based on applying a specified function to the item emitted by the + * current {@code Maybe}, where that function returns a {@link Single}. + * When this {@code Maybe} just completes the resulting {@code Maybe} completes as well. *

- * + * *

*
Scheduler:
*
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the result value type - * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns a - * Single - * @return the Single returned from {@code mapper} when applied to the item emitted by the source Maybe - * @see ReactiveX operators documentation: FlatMap - */ - @CheckReturnValue - @NonNull - @SchedulerSupport(SchedulerSupport.NONE) - public final Single flatMapSingle(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapSingle(this, mapper)); - } - - /** - * Returns a {@link Maybe} based on applying a specified function to the item emitted by the - * source {@link Maybe}, where that function returns a {@link Single}. - * When this Maybe just completes the resulting {@code Maybe} completes as well. - *

- * - *

- *
Scheduler:
- *
{@code flatMapSingleElement} does not operate by default on a particular {@link Scheduler}.
- *
- * *

History: 2.0.2 - experimental * @param the result value type * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns a - * Single - * @return the new Maybe instance + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a + * {@code Single} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe flatMapSingleElement(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapSingleElement(this, mapper)); + public final <@NonNull R> Maybe flatMapSingle(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapSingle<>(this, mapper)); } /** * Returns a {@link Completable} that completes based on applying a specified function to the item emitted by the - * source {@link Maybe}, where that function returns a {@link Completable}. + * current {@code Maybe}, where that function returns a {@code Completable}. *

- * + * *

*
Scheduler:
*
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
* * @param mapper - * a function that, when applied to the item emitted by the source Maybe, returns a - * Completable - * @return the Completable returned from {@code mapper} when applied to the item emitted by the source Maybe + * a function that, when applied to the item emitted by the current {@code Maybe}, returns a + * {@code Completable} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable flatMapCompletable(final Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeFlatMapCompletable(this, mapper)); + public final Completable flatMapCompletable(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlatMapCompletable<>(this, mapper)); } /** - * Hides the identity of this Maybe and its Disposable. + * Hides the identity of this {@code Maybe} and its {@link Disposable}. *

* *

Allows preventing certain identity-based @@ -3302,56 +4126,58 @@ public final Completable flatMapCompletable(final FunctionScheduler: *

{@code hide} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Maybe instance + * @return the new {@code Maybe} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe hide() { - return RxJavaPlugins.onAssembly(new MaybeHide(this)); + return RxJavaPlugins.onAssembly(new MaybeHide<>(this)); } /** - * Ignores the item emitted by the source Maybe and only calls {@code onComplete} or {@code onError}. + * Returns a {@link Completable} that ignores the item emitted by the current {@code Maybe} and only calls {@code onComplete} or {@code onError}. *

- * + * *

*
Scheduler:
*
{@code ignoreElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an empty Completable that only calls {@code onComplete} or {@code onError}, based on which one is - * called by the source Maybe + * @return the new {@code Completable} instance * @see ReactiveX operators documentation: IgnoreElements */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable ignoreElement() { - return RxJavaPlugins.onAssembly(new MaybeIgnoreElementCompletable(this)); + return RxJavaPlugins.onAssembly(new MaybeIgnoreElementCompletable<>(this)); } /** - * Returns a Single that emits {@code true} if the source Maybe is empty, otherwise {@code false}. + * Returns a {@link Single} that emits {@code true} if the current {@code Maybe} is empty, otherwise {@code false}. *

- * + * *

*
Scheduler:
*
{@code isEmpty} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a Boolean + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single isEmpty() { - return RxJavaPlugins.onAssembly(new MaybeIsEmptySingle(this)); + return RxJavaPlugins.onAssembly(new MaybeIsEmptySingle<>(this)); } /** * This method requires advanced knowledge about building operators, please consider * other standard composition methods first; * Returns a {@code Maybe} which, when subscribed to, invokes the {@link MaybeOperator#apply(MaybeObserver) apply(MaybeObserver)} method - * of the provided {@link MaybeOperator} for each individual downstream {@link Maybe} and allows the + * of the provided {@link MaybeOperator} for each individual downstream {@code Maybe} and allows the * insertion of a custom operator by accessing the downstream's {@link MaybeObserver} during this subscription phase * and providing a new {@code MaybeObserver}, containing the custom operator's intended business logic, that will be * used in the subscription process going further upstream. @@ -3404,7 +4230,7 @@ public final Single isEmpty() { * if (str.length() < 2) { * downstream.onSuccess(str); * } else { - * // Maybe is usually expected to produce one of the onXXX events + * // Maybe is expected to produce one of the onXXX events only * downstream.onComplete(); * } * } @@ -3476,36 +4302,37 @@ public final Single isEmpty() { * class and creating a {@link MaybeTransformer} with it is recommended. *

* Note also that it is not possible to stop the subscription phase in {@code lift()} as the {@code apply()} method - * requires a non-null {@code MaybeObserver} instance to be returned, which is then unconditionally subscribed to - * the upstream {@code Maybe}. For example, if the operator decided there is no reason to subscribe to the + * requires a non-{@code null} {@code MaybeObserver} instance to be returned, which is then unconditionally subscribed to + * the current {@code Maybe}. For example, if the operator decided there is no reason to subscribe to the * upstream source because of some optimization possibility or a failure to prepare the operator, it still has to - * return a {@code MaybeObserver} that should immediately dispose the upstream's {@code Disposable} in its + * return a {@code MaybeObserver} that should immediately dispose the upstream's {@link Disposable} in its * {@code onSubscribe} method. Again, using a {@code MaybeTransformer} and extending the {@code Maybe} is * a better option as {@link #subscribeActual} can decide to not subscribe to its upstream after all. *

*
Scheduler:
*
{@code lift} does not operate by default on a particular {@link Scheduler}, however, the - * {@link MaybeOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
+ * {@code MaybeOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
*
* * @param the output value type - * @param lift the {@link MaybeOperator} that receives the downstream's {@code MaybeObserver} and should return + * @param lift the {@code MaybeOperator} that receives the downstream's {@code MaybeObserver} and should return * a {@code MaybeObserver} with custom behavior to be used as the consumer for the current * {@code Maybe}. - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code lift} is {@code null} * @see RxJava wiki: Writing operators * @see #compose(MaybeTransformer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe lift(final MaybeOperator lift) { - ObjectHelper.requireNonNull(lift, "lift is null"); - return RxJavaPlugins.onAssembly(new MaybeLift(this, lift)); + public final <@NonNull R> Maybe lift(@NonNull MaybeOperator lift) { + Objects.requireNonNull(lift, "lift is null"); + return RxJavaPlugins.onAssembly(new MaybeLift<>(this, lift)); } /** - * Returns a Maybe that applies a specified function to the item emitted by the source Maybe and + * Returns a {@code Maybe} that applies a specified function to the item emitted by the current {@code Maybe} and * emits the result of this function application. *

* @@ -3516,44 +4343,46 @@ public final Maybe lift(final MaybeOperator lift) * * @param the result value type * @param mapper - * a function to apply to the item emitted by the Maybe - * @return a Maybe that emits the item from the source Maybe, transformed by the specified function + * a function to apply to the item emitted by the {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: Map */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe map(Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new MaybeMap(this, mapper)); + public final <@NonNull R> Maybe map(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeMap<>(this, mapper)); } /** - * Maps the signal types of this Maybe into a {@link Notification} of the same kind - * and emits it as a single success value to downstream. + * Maps the signal types of this {@code Maybe} into a {@link Notification} of the same kind + * and emits it as a {@link Single}'s {@code onSuccess} value to downstream. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.2.4 - experimental - * @return the new Single instance + * @return the new {@code Single} instance * @since 3.0.0 * @see Single#dematerialize(Function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single> materialize() { - return RxJavaPlugins.onAssembly(new MaybeMaterialize(this)); + return RxJavaPlugins.onAssembly(new MaybeMaterialize<>(this)); } /** - * Flattens this and another Maybe into a single Flowable, without any transformation. + * Flattens this {@code Maybe} and another {@link MaybeSource} into a single {@link Flowable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Maybes so that they appear as a single Flowable, by + * You can combine items emitted by multiple {@code Maybe}s so that they appear as a single {@code Flowable}, by * using the {@code mergeWith} method. *

*
Backpressure:
@@ -3563,33 +4392,35 @@ public final Single> materialize() { *
* * @param other - * a MaybeSource to be merged - * @return a new Flowable instance + * a {@code MaybeSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Merge */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable mergeWith(MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Flowable mergeWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); return merge(this, other); } /** - * Wraps a Maybe to emit its item (or notify of its error) on a specified {@link Scheduler}, + * Wraps a {@code Maybe} to emit its item (or notify of its error) on a specified {@link Scheduler}, * asynchronously. *

- * + * *

*
Scheduler:
- *
you specify which {@link Scheduler} this operator will use.
+ *
you specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to notify subscribers on - * @return the new Maybe instance that its subscribers are notified on the specified - * {@link Scheduler} + * the {@code Scheduler} to notify subscribers on + * @return the new {@code Maybe} instance that its subscribers are notified on the specified + * {@code Scheduler} + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -3597,16 +4428,16 @@ public final Flowable mergeWith(MaybeSource other) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe observeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new MaybeObserveOn(this, scheduler)); + public final Maybe observeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new MaybeObserveOn<>(this, scheduler)); } /** - * Filters the items emitted by a Maybe, only emitting its success value if that - * is an instance of the supplied Class. + * Filters the items emitted by the current {@code Maybe}, only emitting its success value if that + * is an instance of the supplied {@link Class}. *

- * + * *

*
Scheduler:
*
{@code ofType} does not operate by default on a particular {@link Scheduler}.
@@ -3614,21 +4445,24 @@ public final Maybe observeOn(final Scheduler scheduler) { * * @param the output type * @param clazz - * the class type to filter the items emitted by the source Maybe - * @return the new Maybe instance + * the class type to filter the items emitted by the current {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code clazz} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe ofType(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + public final <@NonNull U> Maybe ofType(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return filter(Functions.isInstanceOf(clazz)).cast(clazz); } /** * Calls the specified converter function during assembly time and returns its resulting value. *

+ * + *

* This allows fluent conversion to any other type. *

*
Scheduler:
@@ -3636,113 +4470,152 @@ public final Maybe ofType(final Class clazz) { *
*

History: 2.1.7 - experimental * @param the resulting object type - * @param converter the function that receives the current Maybe instance and returns a value + * @param converter the function that receives the current {@code Maybe} instance and returns a value * @return the converted value - * @throws NullPointerException if converter is null + * @throws NullPointerException if {@code converter} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public final R to(@NonNull MaybeConverter converter) { - return ObjectHelper.requireNonNull(converter, "converter is null").apply(this); + return Objects.requireNonNull(converter, "converter is null").apply(this); } /** - * Converts this Maybe into a backpressure-aware Flowable instance composing cancellation + * Converts this {@code Maybe} into a backpressure-aware {@link Flowable} instance composing cancellation * through. + *

+ * *

*
Backpressure:
- *
The returned Flowable honors the backpressure of the downstream.
+ *
The returned {@code Flowable} honors the backpressure of the downstream.
*
Scheduler:
*
{@code toFlowable} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Flowable instance + * @return the new {@code Flowable} instance */ @SuppressWarnings("unchecked") @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable toFlowable() { if (this instanceof FuseToFlowable) { return ((FuseToFlowable)this).fuseToFlowable(); } - return RxJavaPlugins.onAssembly(new MaybeToFlowable(this)); + return RxJavaPlugins.onAssembly(new MaybeToFlowable<>(this)); } /** - * Converts this Maybe into an Observable instance composing disposal + * Returns a {@link Future} representing the single value emitted by the current {@code Maybe} + * or {@code null} if the current {@code Maybe} is empty. + *

+ * + *

+ * Cancelling the {@code Future} will cancel the subscription to the current {@code Maybe}. + *

+ *
Scheduler:
+ *
{@code toFuture} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @return the new {@code Future} instance + * @see ReactiveX documentation: To + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Future toFuture() { + return subscribeWith(new FutureMultiObserver<>()); + } + + /** + * Converts this {@code Maybe} into an {@link Observable} instance composing disposal * through. + *

+ * *

*
Scheduler:
*
{@code toObservable} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Observable instance + * @return the new {@code Observable} instance */ @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable toObservable() { if (this instanceof FuseToObservable) { return ((FuseToObservable)this).fuseToObservable(); } - return RxJavaPlugins.onAssembly(new MaybeToObservable(this)); + return RxJavaPlugins.onAssembly(new MaybeToObservable<>(this)); } /** - * Converts this Maybe into a Single instance composing disposal - * through and turning an empty Maybe into a signal of NoSuchElementException. + * Converts this {@code Maybe} into a {@link Single} instance composing disposal + * through and turning an empty {@code Maybe} into a signal of {@link NoSuchElementException}. + *

+ * *

*
Scheduler:
*
{@code toSingle} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Single instance + * @return the new {@code Single} instance + * @see #defaultIfEmpty(Object) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single toSingle() { - return RxJavaPlugins.onAssembly(new MaybeToSingle(this, null)); + return RxJavaPlugins.onAssembly(new MaybeToSingle<>(this, null)); } /** - * Returns a Maybe instance that if this Maybe emits an error, it will emit an onComplete + * Returns a {@code Maybe} instance that if this {@code Maybe} emits an error, it will emit an {@code onComplete} * and swallow the throwable. + *

+ * *

*
Scheduler:
*
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Maybe instance + * @return the new {@code Maybe} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe onErrorComplete() { return onErrorComplete(Functions.alwaysTrue()); } /** - * Returns a Maybe instance that if this Maybe emits an error and the predicate returns - * true, it will emit an onComplete and swallow the throwable. + * Returns a {@code Maybe} instance that if this {@code Maybe} emits an error and the predicate returns + * {@code true}, it will emit an {@code onComplete} and swallow the throwable. + *

+ * *

*
Scheduler:
*
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
*
- * @param predicate the predicate to call when an Throwable is emitted which should return true - * if the Throwable should be swallowed and replaced with an onComplete. - * @return the new Maybe instance + * @param predicate the predicate to call when an {@link Throwable} is emitted which should return {@code true} + * if the {@code Throwable} should be swallowed and replaced with an {@code onComplete}. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe onErrorComplete(final Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); + public final Maybe onErrorComplete(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new MaybeOnErrorComplete(this, predicate)); + return RxJavaPlugins.onAssembly(new MaybeOnErrorComplete<>(this, predicate)); } /** - * Instructs a Maybe to pass control to another {@link MaybeSource} rather than invoking - * {@link MaybeObserver#onError onError} if it encounters an error. + * Resumes the flow with the given {@link MaybeSource} when the current {@code Maybe} fails instead of + * signaling the error via {@code onError}. *

- * + * *

* You can use this to prevent errors from propagating or to supply fallback data should errors be * encountered. @@ -3751,25 +4624,26 @@ public final Maybe onErrorComplete(final Predicate predica *

{@code onErrorResumeWith} does not operate by default on a particular {@link Scheduler}.
*
* - * @param next - * the next {@code MaybeSource} that will take over if the source Maybe encounters + * @param fallback + * the next {@code MaybeSource} that will take over if the current {@code Maybe} encounters * an error - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code fallback} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe onErrorResumeWith(final MaybeSource next) { - ObjectHelper.requireNonNull(next, "next is null"); - return onErrorResumeNext(Functions.justFunction(next)); + public final Maybe onErrorResumeWith(@NonNull MaybeSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return onErrorResumeNext(Functions.justFunction(fallback)); } /** - * Instructs a Maybe to pass control to another Maybe rather than invoking - * {@link MaybeObserver#onError onError} if it encounters an error. + * Resumes the flow with a {@link MaybeSource} returned for the failure {@link Throwable} of the current {@code Maybe} by a + * function instead of signaling the error via {@code onError}. *

- * + * *

* You can use this to prevent errors from propagating or to supply fallback data should errors be * encountered. @@ -3778,25 +4652,26 @@ public final Maybe onErrorResumeWith(final MaybeSource next) { *

{@code onErrorResumeNext} does not operate by default on a particular {@link Scheduler}.
*
* - * @param resumeFunction - * a function that returns a MaybeSource that will take over if the source Maybe encounters + * @param fallbackSupplier + * a function that returns a {@code MaybeSource} that will take over if the current {@code Maybe} encounters * an error - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code fallbackSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe onErrorResumeNext(Function> resumeFunction) { - ObjectHelper.requireNonNull(resumeFunction, "resumeFunction is null"); - return RxJavaPlugins.onAssembly(new MaybeOnErrorNext(this, resumeFunction, true)); + public final Maybe onErrorResumeNext(@NonNull Function> fallbackSupplier) { + Objects.requireNonNull(fallbackSupplier, "fallbackSupplier is null"); + return RxJavaPlugins.onAssembly(new MaybeOnErrorNext<>(this, fallbackSupplier)); } /** - * Instructs a Maybe to emit an item (returned by a specified function) rather than invoking - * {@link MaybeObserver#onError onError} if it encounters an error. + * Ends the flow with a success item returned by a function for the {@link Throwable} error signaled by the current + * {@code Maybe} instead of signaling the error via {@code onError}. *

- * + * *

* You can use this to prevent errors from propagating or to supply fallback data should errors be * encountered. @@ -3805,25 +4680,25 @@ public final Maybe onErrorResumeNext(Function{@code onErrorReturn} does not operate by default on a particular {@link Scheduler}.

*
* - * @param valueSupplier + * @param itemSupplier * a function that returns a single value that will be emitted as success value - * the current Maybe signals an onError event - * @return the new Maybe instance + * the current {@code Maybe} signals an {@code onError} event + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code itemSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe onErrorReturn(Function valueSupplier) { - ObjectHelper.requireNonNull(valueSupplier, "valueSupplier is null"); - return RxJavaPlugins.onAssembly(new MaybeOnErrorReturn(this, valueSupplier)); + public final Maybe onErrorReturn(@NonNull Function itemSupplier) { + Objects.requireNonNull(itemSupplier, "itemSupplier is null"); + return RxJavaPlugins.onAssembly(new MaybeOnErrorReturn<>(this, itemSupplier)); } /** - * Instructs a Maybe to emit an item (returned by a specified function) rather than invoking - * {@link MaybeObserver#onError onError} if it encounters an error. + * Ends the flow with the given success item when the current {@code Maybe} fails instead of signaling the error via {@code onError}. *

- * + * *

* You can use this to prevent errors from propagating or to supply fallback data should errors be * encountered. @@ -3833,70 +4708,42 @@ public final Maybe onErrorReturn(Function val *

* * @param item - * the value that is emitted as onSuccess in case this Maybe signals an onError - * @return the new Maybe instance + * the value that is emitted as {@code onSuccess} in case the current {@code Maybe} signals an {@code onError} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe onErrorReturnItem(final T item) { - ObjectHelper.requireNonNull(item, "item is null"); + public final Maybe onErrorReturnItem(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); return onErrorReturn(Functions.justFunction(item)); } /** - * Instructs a Maybe to pass control to another MaybeSource rather than invoking - * {@link MaybeObserver#onError onError} if it encounters an {@link java.lang.Exception}. - *

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

- * - *

- * You can use this to prevent exceptions from propagating or to supply fallback data should exceptions be - * encountered. - *

- *
Scheduler:
- *
{@code onExceptionResumeNext} does not operate by default on a particular {@link Scheduler}.
- *
- * - * @param next - * the next MaybeSource that will take over if the source Maybe encounters - * an exception - * @return the new Maybe instance - * @see ReactiveX operators documentation: Catch - */ - @CheckReturnValue - @NonNull - @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe onExceptionResumeNext(final MaybeSource next) { - ObjectHelper.requireNonNull(next, "next is null"); - return RxJavaPlugins.onAssembly(new MaybeOnErrorNext(this, Functions.justFunction(next), false)); - } - - /** - * Nulls out references to the upstream producer and downstream MaybeObserver if - * the sequence is terminated or downstream calls dispose(). + * Nulls out references to the upstream producer and downstream {@link MaybeObserver} if + * the sequence is terminated or downstream calls {@code dispose()}. *

* *

*
Scheduler:
*
{@code onTerminateDetach} does not operate by default on a particular {@link Scheduler}.
*
- * @return a Maybe which nulls out references to the upstream producer and downstream MaybeObserver if - * the sequence is terminated or downstream calls dispose() + * @return the new {@code Maybe} instance + * the sequence is terminated or downstream calls {@code dispose()} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe onTerminateDetach() { - return RxJavaPlugins.onAssembly(new MaybeDetach(this)); + return RxJavaPlugins.onAssembly(new MaybeDetach<>(this)); } /** - * Returns a Flowable that repeats the sequence of items emitted by the source Maybe indefinitely. + * Returns a {@link Flowable} that repeats the sequence of items emitted by the current {@code Maybe} indefinitely. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure.
@@ -3904,21 +4751,22 @@ public final Maybe onTerminateDetach() { *
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Flowable that emits the items emitted by the source Maybe repeatedly and in sequence + * @return the new {@code Flowable} instance * @see ReactiveX operators documentation: Repeat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable repeat() { return repeat(Long.MAX_VALUE); } /** - * Returns a Flowable that repeats the sequence of items emitted by the source Maybe at most + * Returns a {@link Flowable} that repeats the sequence of items emitted by the current {@code Maybe} at most * {@code count} times. *

- * + * *

*
Backpressure:
*
This operator honors downstream backpressure.
@@ -3927,26 +4775,26 @@ public final Flowable repeat() { *
* * @param times - * the number of times the source Maybe items are repeated, a count of 0 will yield an empty + * the number of times the current {@code Maybe} items are repeated, a count of 0 will yield an empty * sequence - * @return a Flowable that repeats the sequence of items emitted by the source Maybe at most - * {@code count} times + * @return the new {@code Flowable} instance * @throws IllegalArgumentException - * if {@code count} is less than zero + * if {@code times} is negative * @see ReactiveX operators documentation: Repeat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable repeat(long times) { return toFlowable().repeat(times); } /** - * Returns a Flowable that repeats the sequence of items emitted by the source Maybe until - * the provided stop function returns true. + * Returns a {@link Flowable} that repeats the sequence of items emitted by the current {@code Maybe} until + * the provided stop function returns {@code true}. *

- * + * *

*
Backpressure:
*
This operator honors downstream backpressure.
@@ -3955,76 +4803,80 @@ public final Flowable repeat(long times) { *
* * @param stop - * a boolean supplier that is called when the current Flowable completes and unless it returns - * false, the current Flowable is resubscribed - * @return the new Flowable instance + * a boolean supplier that is called when the current {@code Flowable} completes and unless it returns + * {@code false}, the current {@code Flowable} is resubscribed + * @return the new {@code Flowable} instance * @throws NullPointerException - * if {@code stop} is null + * if {@code stop} is {@code null} * @see ReactiveX operators documentation: Repeat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable repeatUntil(BooleanSupplier stop) { + @NonNull + public final Flowable repeatUntil(@NonNull BooleanSupplier stop) { return toFlowable().repeatUntil(stop); } /** - * Returns a Flowable that emits the same values as the source Publisher with the exception of an + * Returns a {@link Flowable} that emits the same values as the current {@code Maybe} with the exception of an * {@code onComplete}. An {@code onComplete} notification from the source will result in the emission of - * a {@code void} item to the Publisher provided as an argument to the {@code notificationHandler} - * function. If that Publisher calls {@code onComplete} or {@code onError} then {@code repeatWhen} will - * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, this Publisher will - * resubscribe to the source Publisher. + * a {@code void} item to the {@code Flowable} provided as an argument to the {@code notificationHandler} + * function. If that {@link Publisher} calls {@code onComplete} or {@code onError} then {@code repeatWhen} will + * call {@code onComplete} or {@code onError} on the child observer. Otherwise, this operator will + * resubscribe to the current {@code Maybe}. *

- * + * *

*
Backpressure:
*
The operator honors downstream backpressure and expects the source {@code Publisher} to honor backpressure as well. - * If this expectation is violated, the operator may throw an {@code IllegalStateException}.
+ * If this expectation is violated, the operator may throw an {@link IllegalStateException}. *
Scheduler:
*
{@code repeatWhen} does not operate by default on a particular {@link Scheduler}.
*
* * @param handler - * receives a Publisher of notifications with which a user can complete or error, aborting the repeat. - * @return the source Publisher modified with repeat logic + * receives a {@code Publisher} of notifications with which a user can complete or error, aborting the repeat. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code handler} is {@code null} * @see ReactiveX operators documentation: Repeat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable repeatWhen(final Function, ? extends Publisher> handler) { + @NonNull + public final Flowable repeatWhen(@NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { return toFlowable().repeatWhen(handler); } /** - * Returns a Maybe that mirrors the source Maybe, resubscribing to it if it calls {@code onError} + * Returns a {@code Maybe} that mirrors the current {@code Maybe}, resubscribing to it if it calls {@code onError} * (infinite retry count). *

- * + * *

- * If the source Maybe calls {@link MaybeObserver#onError}, this method will resubscribe to the source - * Maybe rather than propagating the {@code onError} call. + * If the current {@code Maybe} calls {@link MaybeObserver#onError}, this operator will resubscribe to the current + * {@code Maybe} rather than propagating the {@code onError} call. *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Maybe instance + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe retry() { return retry(Long.MAX_VALUE, Functions.alwaysTrue()); } /** - * Returns a Maybe that mirrors the source Maybe, resubscribing to it if it calls {@code onError} - * and the predicate returns true for that specific exception and retry count. + * Returns a {@code Maybe} that mirrors the current {@code Maybe}, resubscribing to it if it calls {@code onError} + * and the predicate returns {@code true} for that specific exception and retry count. *

- * + * *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
@@ -4033,100 +4885,115 @@ public final Maybe retry() { * @param predicate * the predicate that determines if a resubscription may happen in case of a specific exception * and retry count - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see #retry() * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe retry(BiPredicate predicate) { + @NonNull + public final Maybe retry(@NonNull BiPredicate predicate) { return toFlowable().retry(predicate).singleElement(); } /** - * Returns a Maybe that mirrors the source Maybe, resubscribing to it if it calls {@code onError} + * Returns a {@code Maybe} that mirrors the current {@code Maybe}, resubscribing to it if it calls {@code onError} * up to a specified number of retries. *

- * + * *

- * If the source Maybe calls {@link MaybeObserver#onError}, this method will resubscribe to the source - * Maybe for a maximum of {@code count} resubscriptions rather than propagating the + * If the current {@code Maybe} calls {@link MaybeObserver#onError}, this operator will resubscribe to the current + * {@code Maybe} for a maximum of {@code count} resubscriptions rather than propagating the * {@code onError} call. *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @param count - * the number of times to resubscribe if the current Maybe fails - * @return the new Maybe instance + * @param times + * the number of times to resubscribe if the current {@code Maybe} fails + * @return the new {@code Maybe} instance + * @throws IllegalArgumentException if {@code times} is negative * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe retry(long count) { - return retry(count, Functions.alwaysTrue()); + @NonNull + public final Maybe retry(long times) { + return retry(times, Functions.alwaysTrue()); } /** - * Retries at most times or until the predicate returns false, whichever happens first. - * + * Retries at most {@code times} or until the predicate returns {@code false}, whichever happens first. + *

+ * *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param times the number of times to resubscribe if the current Maybe fails - * @param predicate the predicate called with the failure Throwable and should return true to trigger a retry. - * @return the new Maybe instance + * @param times the number of times to resubscribe if the current {@code Maybe} fails + * @param predicate the predicate called with the failure {@link Throwable} and should return {@code true} to trigger a retry. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code predicate} is {@code null} + * @throws IllegalArgumentException if {@code times} is negative */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe retry(long times, Predicate predicate) { + @NonNull + public final Maybe retry(long times, @NonNull Predicate predicate) { return toFlowable().retry(times, predicate).singleElement(); } /** - * Retries the current Maybe if it fails and the predicate returns true. + * Retries the current {@code Maybe} if it fails and the predicate returns {@code true}. + *

+ * *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @param predicate the predicate that receives the failure Throwable and should return true to trigger a retry. - * @return the new Maybe instance + * @param predicate the predicate that receives the failure {@link Throwable} and should return {@code true} to trigger a retry. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe retry(Predicate predicate) { + @NonNull + public final Maybe retry(@NonNull Predicate predicate) { return retry(Long.MAX_VALUE, predicate); } /** - * Retries until the given stop function returns true. + * Retries until the given stop function returns {@code true}. + *

+ * *

*
Scheduler:
*
{@code retryUntil} does not operate by default on a particular {@link Scheduler}.
*
- * @param stop the function that should return true to stop retrying - * @return the new Maybe instance + * @param stop the function that should return {@code true} to stop retrying + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code stop} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe retryUntil(final BooleanSupplier stop) { - ObjectHelper.requireNonNull(stop, "stop is null"); + public final Maybe retryUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); return retry(Long.MAX_VALUE, Functions.predicateReverseFor(stop)); } /** - * Returns a Maybe that emits the same values as the source Maybe with the exception of an + * Returns a {@code Maybe} that emits the same values as the current {@code Maybe} with the exception of an * {@code onError}. An {@code onError} notification from the source will result in the emission of a - * {@link Throwable} item to the Publisher provided as an argument to the {@code notificationHandler} - * function. If that Publisher calls {@code onComplete} or {@code onError} then {@code retry} will call - * {@code onComplete} or {@code onError} on the child subscription. Otherwise, this Publisher will - * resubscribe to the source Publisher. + * {@link Throwable} item to the {@link Flowable} provided as an argument to the {@code notificationHandler} + * function. If the returned {@link Publisher} calls {@code onComplete} or {@code onError} then {@code retry} will call + * {@code onComplete} or {@code onError} on the child subscription. Otherwise, this operator will + * resubscribe to the current {@code Maybe}. *

- * + * *

* Example: * @@ -4159,7 +5026,7 @@ public final Maybe retryUntil(final BooleanSupplier stop) { * Note that the inner {@code Publisher} returned by the handler function should signal * either {@code onNext}, {@code onError} or {@code onComplete} in response to the received * {@code Throwable} to indicate the operator should retry or terminate. If the upstream to - * the operator is asynchronous, signalling onNext followed by onComplete immediately may + * the operator is asynchronous, signalling {@code onNext} followed by {@code onComplete} immediately may * result in the sequence to be completed immediately. Similarly, if this inner * {@code Publisher} signals {@code onError} or {@code onComplete} while the upstream is * active, the sequence is terminated with the same signal immediately. @@ -4186,65 +5053,217 @@ public final Maybe retryUntil(final BooleanSupplier stop) { *

* * @param handler - * receives a Publisher of notifications with which a user can complete or error, aborting the + * receives a {@code Publisher} of notifications with which a user can complete or error, aborting the * retry - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code handler} is {@code null} * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe retryWhen( - final Function, ? extends Publisher> handler) { + @NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { return toFlowable().retryWhen(handler).singleElement(); } /** - * Subscribes to a Maybe and ignores {@code onSuccess} and {@code onComplete} emissions. + * Wraps the given {@link MaybeObserver}, catches any {@link RuntimeException}s thrown by its + * {@link MaybeObserver#onSubscribe(Disposable)}, {@link MaybeObserver#onSuccess(Object)}, + * {@link MaybeObserver#onError(Throwable)} or {@link MaybeObserver#onComplete()} methods + * and routes those to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. *

- * If the Maybe emits an error, it is wrapped into an + * By default, the {@code Maybe} protocol forbids the {@code onXXX} methods to throw, but some + * {@code MaybeObserver} implementation may do it anyway, causing undefined behavior in the + * upstream. This method and the underlying safe wrapper ensures such misbehaving consumers don't + * disrupt the protocol. + *

+ *
Scheduler:
+ *
{@code safeSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param observer the potentially misbehaving {@code MaybeObserver} + * @throws NullPointerException if {@code observer} is {@code null} + * @see #subscribe(Consumer,Consumer, Action) + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void safeSubscribe(@NonNull MaybeObserver observer) { + Objects.requireNonNull(observer, "observer is null"); + subscribe(new SafeMaybeObserver<>(observer)); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link CompletableSource} + * then the current {@code Maybe} if the other completed normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code CompletableSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Completable.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link SingleSource} + * then the current {@code Maybe} if the other succeeded normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code SingleSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Single.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link MaybeSource} + * then the current {@code Maybe} if the other succeeded or completed normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code MaybeSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Maybe.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns an {@link Observable} which first delivers the events + * of the other {@link ObservableSource} then runs the current {@code Maybe}. + *

+ * + *

+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code ObservableSource} to run first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Observable startWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); + return Observable.wrap(other).concatWith(this.toObservable()); + } + + /** + * Returns a {@link Flowable} which first delivers the events + * of the other {@link Publisher} then runs the current {@code Maybe}. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer + * and expects the other {@code Publisher} to honor it as well.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code Publisher} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final Flowable startWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return toFlowable().startWith(other); + } + + /** + * Subscribes to a {@code Maybe} and ignores {@code onSuccess} and {@code onComplete} emissions. + *

+ * If the {@code Maybe} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Maybe has finished sending them + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Disposable subscribe() { return subscribe(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to a Maybe and provides a callback to handle the items it emits. + * Subscribes to a {@code Maybe} and provides a callback to handle the items it emits. *

- * If the Maybe emits an error, it is wrapped into an + * If the {@code Maybe} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onSuccess - * the {@code Consumer} you have designed to accept a success value from the Maybe - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Maybe has finished sending them + * the {@code Consumer} you have designed to accept a success value from the {@code Maybe} + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @throws NullPointerException - * if {@code onSuccess} is null + * if {@code onSuccess} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onSuccess) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onSuccess) { return subscribe(onSuccess, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to a Maybe and provides callbacks to handle the items it emits and any error + * Subscribes to a {@code Maybe} and provides callbacks to handle the items it emits and any error * notification it issues. *
*
Scheduler:
@@ -4252,25 +5271,26 @@ public final Disposable subscribe(Consumer onSuccess) { *
* * @param onSuccess - * the {@code Consumer} you have designed to accept a success value from the Maybe + * the {@code Consumer} you have designed to accept a success value from the {@code Maybe} * @param onError * the {@code Consumer} you have designed to accept any error notification from the - * Maybe - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Maybe has finished sending them - * @see ReactiveX operators documentation: Subscribe + * {@code Maybe} + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @throws NullPointerException - * if {@code onSuccess} is null, or - * if {@code onError} is null + * if {@code onSuccess} is {@code null}, or + * if {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onSuccess, Consumer onError) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onSuccess, @NonNull Consumer onError) { return subscribe(onSuccess, onError, Functions.EMPTY_ACTION); } /** - * Subscribes to a Maybe and provides callbacks to handle the items it emits and any error or + * Subscribes to a {@code Maybe} and provides callbacks to handle the items it emits and any error or * completion notification it issues. *
*
Scheduler:
@@ -4278,40 +5298,80 @@ public final Disposable subscribe(Consumer onSuccess, Consumer * * @param onSuccess - * the {@code Consumer} you have designed to accept a success value from the Maybe + * the {@code Consumer} you have designed to accept a success value from the {@code Maybe} * @param onError * the {@code Consumer} you have designed to accept any error notification from the - * Maybe + * {@code Maybe} * @param onComplete - * the {@code Action} you have designed to accept a completion notification from the - * Maybe - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the Maybe has finished sending them + * the {@link Action} you have designed to accept a completion notification from the + * {@code Maybe} + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @throws NullPointerException - * if {@code onSuccess} is null, or - * if {@code onError} is null, or - * if {@code onComplete} is null + * if {@code onSuccess}, {@code onError} or + * {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onSuccess, Consumer onError, - Action onComplete) { - ObjectHelper.requireNonNull(onSuccess, "onSuccess is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - return subscribeWith(new MaybeCallbackObserver(onSuccess, onError, onComplete)); + public final Disposable subscribe(@NonNull Consumer onSuccess, @NonNull Consumer onError, + @NonNull Action onComplete) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + return subscribeWith(new MaybeCallbackObserver<>(onSuccess, onError, onComplete)); + } + + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link MaybeObserver}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code MaybeObserver} is removed + * from the given composite. + *

+ * The {@code MaybeObserver} will be removed after the callback for the terminal event has been invoked. + *

+ *
Scheduler:
+ *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param onSuccess the callback for upstream items + * @param onError the callback for an upstream error + * @param onComplete the callback for an upstream completion without any value or error + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code MaybeObserver} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onSuccess}, {@code onError}, + * {@code onComplete} or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onSuccess, + @NonNull Consumer onError, + @NonNull Action onComplete, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseMultiObserver observer = new DisposableAutoReleaseMultiObserver<>( + container, onSuccess, onError, onComplete); + container.add(observer); + subscribe(observer); + return observer; } @SchedulerSupport(SchedulerSupport.NONE) @Override - public final void subscribe(MaybeObserver observer) { - ObjectHelper.requireNonNull(observer, "observer is null"); + public final void subscribe(@NonNull MaybeObserver observer) { + Objects.requireNonNull(observer, "observer is null"); observer = RxJavaPlugins.onSubscribe(this, observer); - ObjectHelper.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null MaybeObserver. Please check the handler provided to RxJavaPlugins.setOnMaybeSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); + Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null MaybeObserver. Please check the handler provided to RxJavaPlugins.setOnMaybeSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); try { subscribeActual(observer); @@ -4330,22 +5390,23 @@ public final void subscribe(MaybeObserver observer) { *

There is no need to call any of the plugin hooks on the current {@code Maybe} instance or * the {@code MaybeObserver}; all hooks and basic safeguards have been * applied by {@link #subscribe(MaybeObserver)} before this method gets called. - * @param observer the MaybeObserver to handle, not null + * @param observer the {@code MaybeObserver} to handle, not {@code null} */ - protected abstract void subscribeActual(MaybeObserver observer); + protected abstract void subscribeActual(@NonNull MaybeObserver observer); /** - * Asynchronously subscribes subscribers to this Maybe on the specified {@link Scheduler}. + * Asynchronously subscribes subscribers to this {@code Maybe} on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
you specify which {@link Scheduler} this operator will use.
+ *
you specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to perform subscription actions on - * @return the new Maybe instance that its subscriptions happen on the specified {@link Scheduler} + * the {@code Scheduler} to perform subscription actions on + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn * @see RxJava Threading Examples * @see #observeOn @@ -4353,14 +5414,14 @@ public final void subscribe(MaybeObserver observer) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe subscribeOn(Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new MaybeSubscribeOn(this, scheduler)); + public final Maybe subscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new MaybeSubscribeOn<>(this, scheduler)); } /** - * Subscribes a given MaybeObserver (subclass) to this Maybe and returns the given - * MaybeObserver as is. + * Subscribes a given {@link MaybeObserver} (subclass) to this {@code Maybe} and returns the given + * {@code MaybeObserver} as is. *

Usage example: *


      * Maybe<Integer> source = Maybe.just(1);
@@ -4376,96 +5437,98 @@ public final Maybe subscribeOn(Scheduler scheduler) {
      *  
Scheduler:
*
{@code subscribeWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param the type of the MaybeObserver to use and return - * @param observer the MaybeObserver (subclass) to use and return, not null - * @return the input {@code subscriber} - * @throws NullPointerException if {@code subscriber} is null + * @param the type of the {@code MaybeObserver} to use and return + * @param observer the {@code MaybeObserver} (subclass) to use and return, not {@code null} + * @return the input {@code observer} + * @throws NullPointerException if {@code observer} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > E subscribeWith(E observer) { + @NonNull + public final <@NonNull E extends MaybeObserver> E subscribeWith(E observer) { subscribe(observer); return observer; } /** - * Returns a Maybe that emits the items emitted by the source Maybe or the items of an alternate - * MaybeSource if the current Maybe is empty. + * Returns a {@code Maybe} that emits the items emitted by the current {@code Maybe} or the items of an alternate + * {@link MaybeSource} if the current {@code Maybe} is empty. *

- * + * *

*
Scheduler:
*
{@code switchIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * the alternate MaybeSource to subscribe to if the main does not emit any items - * @return a Maybe that emits the items emitted by the source Maybe or the items of an - * alternate MaybeSource if the source Maybe is empty. + * the alternate {@code MaybeSource} to subscribe to if the main does not emit any items + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code other} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe switchIfEmpty(MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new MaybeSwitchIfEmpty(this, other)); + public final Maybe switchIfEmpty(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new MaybeSwitchIfEmpty<>(this, other)); } /** - * Returns a Single that emits the items emitted by the source Maybe or the item of an alternate - * SingleSource if the current Maybe is empty. + * Returns a {@link Single} that emits the items emitted by the current {@code Maybe} or the item of an alternate + * {@link SingleSource} if the current {@code Maybe} is empty. *

- * + * *

*
Scheduler:
*
{@code switchIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.4 - experimental * @param other - * the alternate SingleSource to subscribe to if the main does not emit any items - * @return a Single that emits the items emitted by the source Maybe or the item of an - * alternate SingleSource if the source Maybe is empty. + * the alternate {@code SingleSource} to subscribe to if the main does not emit any items + * @return the new {@code Single} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single switchIfEmpty(SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new MaybeSwitchIfEmptySingle(this, other)); + public final Single switchIfEmpty(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new MaybeSwitchIfEmptySingle<>(this, other)); } /** - * Returns a Maybe that emits the items emitted by the source Maybe until a second MaybeSource + * Returns a {@code Maybe} that emits the items emitted by the current {@code Maybe} until a second {@link MaybeSource} * emits an item. *

- * + * *

*
Scheduler:
*
{@code takeUntil} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * the MaybeSource whose first emitted item will cause {@code takeUntil} to stop emitting items - * from the source Maybe + * the {@code MaybeSource} whose first emitted item will cause {@code takeUntil} to stop emitting items + * from the current {@code Maybe} * @param * the type of items emitted by {@code other} - * @return a Maybe that emits the items emitted by the source Maybe until such time as {@code other} emits its first item + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe takeUntil(MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new MaybeTakeUntilMaybe(this, other)); + public final <@NonNull U> Maybe takeUntil(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new MaybeTakeUntilMaybe<>(this, other)); } /** - * Returns a Maybe that emits the item emitted by the source Maybe until a second Publisher + * Returns a {@code Maybe} that emits the item emitted by the current {@code Maybe} until a second {@link Publisher} * emits an item. *

- * + * *

*
Backpressure:
*
The {@code Publisher} is consumed in an unbounded fashion and is cancelled after the first item @@ -4475,28 +5538,255 @@ public final Maybe takeUntil(MaybeSource other) { *
* * @param other - * the Publisher whose first emitted item will cause {@code takeUntil} to stop emitting items - * from the source Publisher + * the {@code Publisher} whose first emitted item will cause {@code takeUntil} to stop emitting items + * from the source {@code Publisher} * @param * the type of items emitted by {@code other} - * @return a Maybe that emits the items emitted by the source Maybe until such time as {@code other} emits its first item + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe takeUntil(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new MaybeTakeUntilPublisher(this, other)); + public final <@NonNull U> Maybe takeUntil(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new MaybeTakeUntilPublisher<>(this, other)); + } + + /** + * Measures the time (in milliseconds) between the subscription and success item emission + * of the current {@code Maybe} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timeInterval()}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the {@code computation} {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @return the new {@code Maybe} instance + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Maybe> timeInterval() { + return timeInterval(TimeUnit.MILLISECONDS, Schedulers.computation()); + } + + /** + * Measures the time (in milliseconds) between the subscription and success item emission + * of the current {@code Maybe} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timeInterval(Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the provided {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Maybe> timeInterval(@NonNull Scheduler scheduler) { + return timeInterval(TimeUnit.MILLISECONDS, scheduler); + } + + /** + * Measures the time between the subscription and success item emission + * of the current {@code Maybe} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timeInterval(TimeUnit)}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the {@code computation} {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @param unit the time unit for measurement + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Maybe> timeInterval(@NonNull TimeUnit unit) { + return timeInterval(unit, Schedulers.computation()); + } + + /** + * Measures the time between the subscription and success item emission + * of the current {@code Maybe} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timeInterval(TimeUnit, Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the provided {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @param unit the time unit for measurement + * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Maybe> timeInterval(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new MaybeTimeInterval<>(this, unit, scheduler, true)); + } + + /** + * Combines the success value from the current {@code Maybe} with the current time (in milliseconds) of + * its reception, using the {@code computation} {@link Scheduler} as time source, + * then signals them as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timestamp()}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the {@code computation} {@code Scheduler} + * for determining the current time upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @return the new {@code Maybe} instance + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Maybe> timestamp() { + return timestamp(TimeUnit.MILLISECONDS, Schedulers.computation()); + } + + /** + * Combines the success value from the current {@code Maybe} with the current time (in milliseconds) of + * its reception, using the given {@link Scheduler} as time source, + * then signals them as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timestamp(Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the provided {@code Scheduler} + * for determining the current time upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Maybe> timestamp(@NonNull Scheduler scheduler) { + return timestamp(TimeUnit.MILLISECONDS, scheduler); + } + + /** + * Combines the success value from the current {@code Maybe} with the current time of + * its reception, using the {@code computation} {@link Scheduler} as time source, + * then signals it as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timestamp(TimeUnit)}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the {@code computation} {@code Scheduler}, + * for determining the current time upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @param unit the time unit for measurement + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Maybe> timestamp(@NonNull TimeUnit unit) { + return timestamp(unit, Schedulers.computation()); + } + + /** + * Combines the success value from the current {@code Maybe} with the current time of + * its reception, using the given {@link Scheduler} as time source, + * then signals it as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Maybe} is empty or fails, the resulting {@code Maybe} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link Single#timestamp(TimeUnit, Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the provided {@code Scheduler}, + * which is used for determining the current time upon receiving the + * success item from the current {@code Maybe}.
+ *
+ * @param unit the time unit for measurement + * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Maybe> timestamp(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new MaybeTimeInterval<>(this, unit, scheduler, false)); } /** - * Returns a Maybe that mirrors the source Maybe but applies a timeout policy for each emitted + * Returns a {@code Maybe} that mirrors the current {@code Maybe} but applies a timeout policy for each emitted * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, - * the resulting Maybe terminates and notifies MaybeObservers of a {@code TimeoutException}. + * the resulting {@code Maybe} terminates and notifies {@link MaybeObserver}s of a {@link TimeoutException}. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -4504,23 +5794,25 @@ public final Maybe takeUntil(Publisher other) { * * @param timeout * maximum duration between emitted items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument. - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Maybe timeout(long timeout, TimeUnit timeUnit) { - return timeout(timeout, timeUnit, Schedulers.computation()); + @NonNull + public final Maybe timeout(long timeout, @NonNull TimeUnit unit) { + return timeout(timeout, unit, Schedulers.computation()); } /** - * Returns a Maybe that mirrors the source Maybe but applies a timeout policy for each emitted + * Returns a {@code Maybe} that mirrors the current {@code Maybe} but applies a timeout policy for each emitted * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, - * the source MaybeSource is disposed and resulting Maybe begins instead to mirror a fallback MaybeSource. + * the current {@code Maybe} is disposed and resulting {@code Maybe} begins instead to mirror a fallback {@link MaybeSource}. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -4528,103 +5820,112 @@ public final Maybe timeout(long timeout, TimeUnit timeUnit) { * * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param fallback - * the fallback MaybeSource to use in case of a timeout - * @return the new Maybe instance + * the fallback {@code MaybeSource} to use in case of a timeout + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code unit} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Maybe timeout(long timeout, TimeUnit timeUnit, MaybeSource fallback) { - ObjectHelper.requireNonNull(fallback, "fallback is null"); - return timeout(timeout, timeUnit, Schedulers.computation(), fallback); + public final Maybe timeout(long timeout, @NonNull TimeUnit unit, @NonNull MaybeSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout(timeout, unit, Schedulers.computation(), fallback); } /** - * Returns a Maybe that mirrors the source Maybe but applies a timeout policy for each emitted - * item using a specified Scheduler. If the next item isn't emitted within the specified timeout duration - * starting from its predecessor, the source MaybeSource is disposed and resulting Maybe begins instead - * to mirror a fallback MaybeSource. + * Returns a {@code Maybe} that mirrors the current {@code Maybe} but applies a timeout policy for each emitted + * item using a specified {@link Scheduler}. If the next item isn't emitted within the specified timeout duration + * starting from its predecessor, the current {@code Maybe} is disposed and resulting {@code Maybe} begins instead + * to mirror a fallback {@link MaybeSource}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param fallback - * the MaybeSource to use as the fallback in case of a timeout + * the {@code MaybeSource} to use as the fallback in case of a timeout * @param scheduler - * the {@link Scheduler} to run the timeout timers on - * @return the new Maybe instance + * the {@code Scheduler} to run the timeout timers on + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code fallback}, {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler, MaybeSource fallback) { - ObjectHelper.requireNonNull(fallback, "fallback is null"); - return timeout(timer(timeout, timeUnit, scheduler), fallback); + public final Maybe timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull MaybeSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout(timer(timeout, unit, scheduler), fallback); } /** - * Returns a Maybe that mirrors the source Maybe but applies a timeout policy for each emitted - * item, where this policy is governed on a specified Scheduler. If the next item isn't emitted within the - * specified timeout duration starting from its predecessor, the resulting Maybe terminates and - * notifies MaybeObservers of a {@code TimeoutException}. + * Returns a {@code Maybe} that mirrors the current {@code Maybe} but applies a timeout policy for each emitted + * item, where this policy is governed on a specified {@link Scheduler}. If the next item isn't emitted within the + * specified timeout duration starting from its predecessor, the resulting {@code Maybe} terminates and + * notifies {@link MaybeObserver}s of a {@link TimeoutException}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param scheduler - * the Scheduler to run the timeout timers on - * @return the new Maybe instance + * the {@code Scheduler} to run the timeout timers on + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: Timeout + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { - return timeout(timer(timeout, timeUnit, scheduler)); + @NonNull + public final Maybe timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return timeout(timer(timeout, unit, scheduler)); } /** * If the current {@code Maybe} didn't signal an event before the {@code timeoutIndicator} {@link MaybeSource} signals, a * {@link TimeoutException} is signaled instead. + *

+ * *

*
Scheduler:
*
{@code timeout} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type of the - * @param timeoutIndicator the {@code MaybeSource} that indicates the timeout by signaling onSuccess - * or onComplete. - * @return the new Maybe instance + * @param timeoutIndicator the {@code MaybeSource} that indicates the timeout by signaling {@code onSuccess} + * or {@code onComplete}. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code timeoutIndicator} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe timeout(MaybeSource timeoutIndicator) { - ObjectHelper.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); - return RxJavaPlugins.onAssembly(new MaybeTimeoutMaybe(this, timeoutIndicator, null)); + public final <@NonNull U> Maybe timeout(@NonNull MaybeSource timeoutIndicator) { + Objects.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); + return RxJavaPlugins.onAssembly(new MaybeTimeoutMaybe<>(this, timeoutIndicator, null)); } /** * If the current {@code Maybe} didn't signal an event before the {@code timeoutIndicator} {@link MaybeSource} signals, * the current {@code Maybe} is disposed and the {@code fallback} {@code MaybeSource} subscribed to * as a continuation. + *

+ * *

*
Scheduler:
*
{@code timeout} does not operate by default on a particular {@link Scheduler}.
@@ -4633,48 +5934,54 @@ public final Maybe timeout(MaybeSource timeoutIndicator) { * @param timeoutIndicator the {@code MaybeSource} that indicates the timeout by signaling {@code onSuccess} * or {@code onComplete}. * @param fallback the {@code MaybeSource} that is subscribed to if the current {@code Maybe} times out - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code timeoutIndicator} or {@code fallback} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe timeout(MaybeSource timeoutIndicator, MaybeSource fallback) { - ObjectHelper.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); - ObjectHelper.requireNonNull(fallback, "fallback is null"); - return RxJavaPlugins.onAssembly(new MaybeTimeoutMaybe(this, timeoutIndicator, fallback)); + public final <@NonNull U> Maybe timeout(@NonNull MaybeSource timeoutIndicator, @NonNull MaybeSource fallback) { + Objects.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); + Objects.requireNonNull(fallback, "fallback is null"); + return RxJavaPlugins.onAssembly(new MaybeTimeoutMaybe<>(this, timeoutIndicator, fallback)); } /** * If the current {@code Maybe} source didn't signal an event before the {@code timeoutIndicator} {@link Publisher} signals, a * {@link TimeoutException} is signaled instead. + *

+ * *

*
Backpressure:
- *
The {@code timeoutIndicator} {@link Publisher} is consumed in an unbounded manner and + *
The {@code timeoutIndicator} {@code Publisher} is consumed in an unbounded manner and * is cancelled after its first item.
*
Scheduler:
*
{@code timeout} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type of the - * @param timeoutIndicator the {@code MaybeSource} that indicates the timeout by signaling {@code onSuccess} + * @param timeoutIndicator the {@code Publisher} that indicates the timeout by signaling {@code onSuccess} * or {@code onComplete}. - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code timeoutIndicator} is {@code null} */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe timeout(Publisher timeoutIndicator) { - ObjectHelper.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); - return RxJavaPlugins.onAssembly(new MaybeTimeoutPublisher(this, timeoutIndicator, null)); + public final <@NonNull U> Maybe timeout(@NonNull Publisher timeoutIndicator) { + Objects.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); + return RxJavaPlugins.onAssembly(new MaybeTimeoutPublisher<>(this, timeoutIndicator, null)); } /** * If the current {@code Maybe} didn't signal an event before the {@code timeoutIndicator} {@link Publisher} signals, - * the current {@code Maybe} is disposed and the {@code fallback} {@code MaybeSource} subscribed to + * the current {@code Maybe} is disposed and the {@code fallback} {@link MaybeSource} subscribed to * as a continuation. + *

+ * *

*
Backpressure:
- *
The {@code timeoutIndicator} {@link Publisher} is consumed in an unbounded manner and + *
The {@code timeoutIndicator} {@code Publisher} is consumed in an unbounded manner and * is cancelled after its first item.
*
Scheduler:
*
{@code timeout} does not operate by default on a particular {@link Scheduler}.
@@ -4683,46 +5990,47 @@ public final Maybe timeout(Publisher timeoutIndicator) { * @param timeoutIndicator the {@code MaybeSource} that indicates the timeout by signaling {@code onSuccess} * or {@code onComplete} * @param fallback the {@code MaybeSource} that is subscribed to if the current {@code Maybe} times out - * @return the new Maybe instance + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code timeoutIndicator} or {@code fallback} is {@code null} */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe timeout(Publisher timeoutIndicator, MaybeSource fallback) { - ObjectHelper.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); - ObjectHelper.requireNonNull(fallback, "fallback is null"); - return RxJavaPlugins.onAssembly(new MaybeTimeoutPublisher(this, timeoutIndicator, fallback)); + public final <@NonNull U> Maybe timeout(@NonNull Publisher timeoutIndicator, @NonNull MaybeSource fallback) { + Objects.requireNonNull(timeoutIndicator, "timeoutIndicator is null"); + Objects.requireNonNull(fallback, "fallback is null"); + return RxJavaPlugins.onAssembly(new MaybeTimeoutPublisher<>(this, timeoutIndicator, fallback)); } /** - * Returns a Maybe which makes sure when a MaybeObserver disposes the Disposable, - * that call is propagated up on the specified scheduler. + * Returns a {@code Maybe} which makes sure when a {@link MaybeObserver} disposes the {@link Disposable}, + * that call is propagated up on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
{@code unsubscribeOn} calls dispose() of the upstream on the {@link Scheduler} you specify.
+ *
{@code unsubscribeOn} calls {@code dispose()} of the upstream on the {@code Scheduler} you specify.
*
* @param scheduler the target scheduler where to execute the disposal - * @return the new Maybe instance - * @throws NullPointerException if scheduler is null + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Maybe unsubscribeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new MaybeUnsubscribeOn(this, scheduler)); + public final Maybe unsubscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new MaybeUnsubscribeOn<>(this, scheduler)); } /** - * Waits until this and the other MaybeSource signal a success value then applies the given BiFunction - * to those values and emits the BiFunction's resulting value to downstream. + * Waits until this and the other {@link MaybeSource} signal a success value then applies the given {@link BiFunction} + * to those values and emits the {@code BiFunction}'s resulting value to downstream. * * * - *

If either this or the other MaybeSource is empty or signals an error, the resulting Maybe will + *

If either this or the other {@code MaybeSource} is empty or signals an error, the resulting {@code Maybe} will * terminate immediately and dispose the other source. * *

@@ -4731,22 +6039,23 @@ public final Maybe unsubscribeOn(final Scheduler scheduler) { *
* * @param - * the type of items emitted by the {@code other} MaybeSource + * the type of items emitted by the {@code other} {@code MaybeSource} * @param - * the type of items emitted by the resulting Maybe + * the type of items emitted by the resulting {@code Maybe} * @param other - * the other MaybeSource + * the other {@code MaybeSource} * @param zipper - * a function that combines the pairs of items from the two MaybeSources to generate the items to - * be emitted by the resulting Maybe - * @return the new Maybe instance + * a function that combines the pairs of items from the two {@code MaybeSource}s to generate the items to + * be emitted by the resulting {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe zipWith(MaybeSource other, BiFunction zipper) { - ObjectHelper.requireNonNull(other, "other is null"); + public final <@NonNull U, @NonNull R> Maybe zipWith(@NonNull MaybeSource other, @NonNull BiFunction zipper) { + Objects.requireNonNull(other, "other is null"); return zip(this, other, zipper); } @@ -4755,36 +6064,38 @@ public final Maybe zipWith(MaybeSource other, BiFunction< // ------------------------------------------------------------------ /** - * Creates a TestObserver and subscribes - * it to this Maybe. + * Creates a {@link TestObserver} and subscribes + * it to this {@code Maybe}. *
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new TestObserver instance + * @return the new {@code TestObserver} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); subscribe(to); return to; } /** - * Creates a TestObserver optionally in cancelled state, then subscribes it to this Maybe. + * Creates a {@link TestObserver} optionally in cancelled state, then subscribes it to this {@code Maybe}. *
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @param dispose if true, the TestObserver will be dispose before subscribing to this - * Maybe. - * @return the new TestObserver instance + * @param dispose if {@code true}, the {@code TestObserver} will be disposed before subscribing to this + * {@code Maybe}. + * @return the new {@code TestObserver} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test(boolean dispose) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); if (dispose) { to.dispose(); @@ -4793,4 +6104,250 @@ public final TestObserver test(boolean dispose) { subscribe(to); return to; } + + // ------------------------------------------------------------------------- + // JDK 8 Support + // ------------------------------------------------------------------------- + + /** + * Converts the existing value of the provided optional into a {@link #just(Object)} + * or an empty optional into an {@link #empty()} {@code Maybe} instance. + *

+ * + *

+ * Note that the operator takes an already instantiated optional reference and does not + * by any means create this original optional. If the optional is to be created per + * consumer upon subscription, use {@link #defer(Supplier)} around {@code fromOptional}: + *


+     * Maybe.defer(() -> Maybe.fromOptional(createOptional()));
+     * 
+ *
+ *
Scheduler:
+ *
{@code fromOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the optional value + * @param optional the optional value to convert into a {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code optional} is {@code null} + * @since 3.0.0 + * @see #just(Object) + * @see #empty() + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Maybe<@NonNull T> fromOptional(@NonNull Optional optional) { + Objects.requireNonNull(optional, "optional is null"); + return optional.map(Maybe::just).orElseGet(Maybe::empty); + } + + /** + * Signals the completion value or error of the given (hot) {@link CompletionStage}-based asynchronous calculation. + *

+ * + *

+ * Note that the operator takes an already instantiated, running or terminated {@code CompletionStage}. + * If the {@code CompletionStage} is to be created per consumer upon subscription, use {@link #defer(Supplier)} + * around {@code fromCompletionStage}: + *


+     * Maybe.defer(() -> Maybe.fromCompletionStage(createCompletionStage()));
+     * 
+ *

+ * If the {@code CompletionStage} completes with {@code null}, the resulting {@code Maybe} is completed via {@code onComplete}. + *

+ * Canceling the flow can't cancel the execution of the {@code CompletionStage} because {@code CompletionStage} + * itself doesn't support cancellation. Instead, the operator detaches from the {@code CompletionStage}. + *

+ *
Scheduler:
+ *
{@code fromCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code CompletionStage} + * @param stage the {@code CompletionStage} to convert to {@code Maybe} and signal its terminal value or error + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code stage} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Maybe<@NonNull T> fromCompletionStage(@NonNull CompletionStage stage) { + Objects.requireNonNull(stage, "stage is null"); + return RxJavaPlugins.onAssembly(new MaybeFromCompletionStage<>(stage)); + } + + /** + * Maps the upstream success value into an {@link Optional} and emits the contained item if not empty. + *

+ * + * + *

+ *
Scheduler:
+ *
{@code mapOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the non-{@code null} output type + * @param mapper the function that receives the upstream success item and should return a non-empty {@code Optional} + * to emit as the success output or an empty {@code Optional} to complete the {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #map(Function) + * @see #filter(Predicate) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Maybe mapOptional(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeMapOptional<>(this, mapper)); + } + + /** + * Signals the upstream success item (or a {@link NoSuchElementException} if the upstream is empty) via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * {@link #toCompletionStage(Object)} with {@code null} or turn the upstream into a sequence of {@link Optional}s and + * default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).toCompletionStage(Optional.empty());
+     * 
+ *
+ *
Scheduler:
+ *
{@code toCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #toCompletionStage(Object) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage toCompletionStage() { + return subscribeWith(new CompletionStageConsumer<>(false, null)); + } + + /** + * Signals the upstream success item (or the default item if the upstream is empty) via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).toCompletionStage(Optional.empty());
+     * 
+ *
+ *
Scheduler:
+ *
{@code toCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage toCompletionStage(@Nullable T defaultItem) { + return subscribeWith(new CompletionStageConsumer<>(true, defaultItem)); + } + + /** + * Maps the upstream succecss value into a Java {@link Stream} and emits its + * items to the downstream consumer as a {@link Flowable}. + *

+ * + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flattenAsFlowable(Function)}: + *


+     * source.flattenAsFlowable(item -> createStream(item)::iterator);
+     * 
+ *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flattenStreamAsFlowable(item -> IntStream.rangeClosed(1, 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and iterates the given {@code Stream} + * on demand (i.e., when requested).
+ *
Scheduler:
+ *
{@code flattenStreamAsFlowable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code Stream} and the output {@code Flowable} + * @param mapper the function that receives the upstream success item and should + * return a {@code Stream} of values to emit. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #flattenAsFlowable(Function) + * @see #flattenStreamAsObservable(Function) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + @NonNull + public final <@NonNull R> Flowable flattenStreamAsFlowable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlattenStreamAsFlowable<>(this, mapper)); + } + + /** + * Maps the upstream succecss value into a Java {@link Stream} and emits its + * items to the downstream consumer as an {@link Observable}. + * + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flattenAsObservable(Function)}: + *


+     * source.flattenAsObservable(item -> createStream(item)::iterator);
+     * 
+ *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flattenStreamAsObservable(item -> IntStream.rangeClosed(1, 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Scheduler:
+ *
{@code flattenStreamAsObservable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code Stream} and the output {@code Observable} + * @param mapper the function that receives the upstream success item and should + * return a {@code Stream} of values to emit. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #flattenAsObservable(Function) + * @see #flattenStreamAsFlowable(Function) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Observable flattenStreamAsObservable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new MaybeFlattenStreamAsObservable<>(this, mapper)); + } } diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeConverter.java b/src/main/java/io/reactivex/rxjava3/core/MaybeConverter.java index de70b81814..6ef529de7e 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeConverter.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeConverter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,18 +16,19 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Convenience interface and callback used by the {@link Maybe#to} operator to turn a Maybe into another + * Convenience interface and callback used by the {@link Maybe#to} operator to turn a {@link Maybe} into another * value fluently. *

History: 2.1.7 - experimental * @param the upstream type * @param the output type * @since 2.2 */ -public interface MaybeConverter { +@FunctionalInterface +public interface MaybeConverter<@NonNull T, @NonNull R> { /** - * Applies a function to the upstream Maybe and returns a converted value of type {@code R}. + * Applies a function to the upstream {@link Maybe} and returns a converted value of type {@code R}. * - * @param upstream the upstream Maybe instance + * @param upstream the upstream {@code Maybe} instance * @return the converted value */ @NonNull diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeEmitter.java b/src/main/java/io/reactivex/rxjava3/core/MaybeEmitter.java index dfc8671073..57d7f5a219 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeEmitter.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeEmitter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.functions.Cancellable; +import io.reactivex.rxjava3.functions.*; /** * Abstraction over an RxJava {@link MaybeObserver} that allows associating @@ -47,7 +47,7 @@ * * @param the value type to emit */ -public interface MaybeEmitter { +public interface MaybeEmitter<@NonNull T> { /** * Signal a success value. @@ -57,7 +57,7 @@ public interface MaybeEmitter { /** * Signal an exception. - * @param t the exception, not null + * @param t the exception, not {@code null} */ void onError(@NonNull Throwable t); @@ -67,16 +67,18 @@ public interface MaybeEmitter { void onComplete(); /** - * Sets a Disposable on this emitter; any previous {@link Disposable} + * Sets a {@link Disposable} on this emitter; any previous {@code Disposable} * or {@link Cancellable} will be disposed/cancelled. - * @param d the disposable, null is allowed + *

This method is thread-safe. + * @param d the disposable, {@code null} is allowed */ void setDisposable(@Nullable Disposable d); /** - * Sets a Cancellable on this emitter; any previous {@link Disposable} - * or {@link Cancellable} will be disposed/cancelled. - * @param c the cancellable resource, null is allowed + * Sets a {@link Cancellable} on this emitter; any previous {@link Disposable} + * or {@code Cancellable} will be disposed/cancelled. + *

This method is thread-safe. + * @param c the {@code Cancellable} resource, {@code null} is allowed */ void setCancellable(@Nullable Cancellable c); @@ -91,14 +93,14 @@ public interface MaybeEmitter { boolean isDisposed(); /** - * Attempts to emit the specified {@code Throwable} error if the downstream + * Attempts to emit the specified {@link Throwable} error if the downstream * hasn't cancelled the sequence or is otherwise terminated, returning false * if the emission is not allowed to happen due to lifecycle restrictions. *

- * Unlike {@link #onError(Throwable)}, the {@code RxJavaPlugins.onError} is not called - * if the error could not be delivered. + * Unlike {@link #onError(Throwable)}, the {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable) RxjavaPlugins.onError} + * is not called if the error could not be delivered. *

History: 2.1.1 - experimental - * @param t the throwable error to signal if possible + * @param t the {@code Throwable} error to signal if possible * @return true if successful, false if the downstream is not able to accept further * events * @since 2.2 diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeObserver.java b/src/main/java/io/reactivex/rxjava3/core/MaybeObserver.java index 7b7eb1a0d4..f6567dc94e 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeObserver.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,35 +53,35 @@ * the type of item the MaybeObserver expects to observe * @since 2.0 */ -public interface MaybeObserver { +public interface MaybeObserver<@NonNull T> { /** - * Provides the MaybeObserver with the means of cancelling (disposing) the - * connection (channel) with the Maybe in both + * Provides the {@link MaybeObserver} with the means of cancelling (disposing) the + * connection (channel) with the {@link Maybe} in both * synchronous (from within {@code onSubscribe(Disposable)} itself) and asynchronous manner. - * @param d the Disposable instance whose {@link Disposable#dispose()} can + * @param d the {@link Disposable} instance whose {@link Disposable#dispose()} can * be called anytime to cancel the connection */ void onSubscribe(@NonNull Disposable d); /** - * Notifies the MaybeObserver with one item and that the {@link Maybe} has finished sending + * Notifies the {@link MaybeObserver} with one item and that the {@link Maybe} has finished sending * push-based notifications. *

* The {@link Maybe} will not call this method if it calls {@link #onError}. * * @param t - * the item emitted by the Maybe + * the item emitted by the {@code Maybe} */ void onSuccess(@NonNull T t); /** - * Notifies the MaybeObserver that the {@link Maybe} has experienced an error condition. + * Notifies the {@link MaybeObserver} that the {@link Maybe} has experienced an error condition. *

* If the {@link Maybe} calls this method, it will not thereafter call {@link #onSuccess}. * * @param e - * the exception encountered by the Maybe + * the exception encountered by the {@code Maybe} */ void onError(@NonNull Throwable e); diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java index dbccf870f9..67994d3d40 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeOnSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,22 +10,24 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link MaybeEmitter} instance that allows pushing + * a {@link MaybeEmitter} instance that allows pushing * an event in a cancellation-safe manner. * * @param the value type pushed */ -public interface MaybeOnSubscribe { +@FunctionalInterface +public interface MaybeOnSubscribe<@NonNull T> { /** - * Called for each MaybeObserver that subscribes. - * @param emitter the safe emitter instance, never null + * Called for each {@link MaybeObserver} that subscribes. + * @param emitter the safe emitter instance, never {@code null} * @throws Throwable on error */ void subscribe(@NonNull MaybeEmitter emitter) throws Throwable; diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeOperator.java b/src/main/java/io/reactivex/rxjava3/core/MaybeOperator.java index b3173eb833..7cea758dd9 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeOperator.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeOperator.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,23 +10,25 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to map/wrap a downstream observer to an upstream observer. + * Interface to map/wrap a downstream {@link MaybeObserver} to an upstream {@code MaybeObserver}. * * @param the value type of the downstream * @param the value type of the upstream */ -public interface MaybeOperator { +@FunctionalInterface +public interface MaybeOperator<@NonNull Downstream, @NonNull Upstream> { /** - * Applies a function to the child MaybeObserver and returns a new parent MaybeObserver. - * @param observer the child MaybeObserver instance - * @return the parent MaybeObserver instance - * @throws Exception on failure + * Applies a function to the child {@link MaybeObserver} and returns a new parent {@code MaybeObserver}. + * @param observer the child {@code MaybeObserver} instance + * @return the parent {@code MaybeObserver} instance + * @throws Throwable on failure */ @NonNull - MaybeObserver apply(@NonNull MaybeObserver observer) throws Exception; + MaybeObserver apply(@NonNull MaybeObserver observer) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeSource.java b/src/main/java/io/reactivex/rxjava3/core/MaybeSource.java index dd037cc864..a15ea2c89d 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeSource.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; @@ -24,12 +25,13 @@ * @param the element type * @since 2.0 */ -public interface MaybeSource { +@FunctionalInterface +public interface MaybeSource<@NonNull T> { /** - * Subscribes the given MaybeObserver to this MaybeSource instance. - * @param observer the MaybeObserver, not null - * @throws NullPointerException if {@code observer} is null + * Subscribes the given {@link MaybeObserver} to this {@link MaybeSource} instance. + * @param observer the {@code MaybeObserver}, not {@code null} + * @throws NullPointerException if {@code observer} is {@code null} */ void subscribe(@NonNull MaybeObserver observer); } diff --git a/src/main/java/io/reactivex/rxjava3/core/MaybeTransformer.java b/src/main/java/io/reactivex/rxjava3/core/MaybeTransformer.java index d53bfda71d..770497fe3c 100644 --- a/src/main/java/io/reactivex/rxjava3/core/MaybeTransformer.java +++ b/src/main/java/io/reactivex/rxjava3/core/MaybeTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,17 +16,18 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to compose Maybes. + * Interface to compose {@link Maybe}s. * * @param the upstream value type * @param the downstream value type */ -public interface MaybeTransformer { +@FunctionalInterface +public interface MaybeTransformer<@NonNull Upstream, @NonNull Downstream> { /** - * Applies a function to the upstream Maybe and returns a MaybeSource with + * Applies a function to the upstream {@link Maybe} and returns a {@link MaybeSource} with * optionally different element type. - * @param upstream the upstream Maybe instance - * @return the transformed MaybeSource instance + * @param upstream the upstream {@code Maybe} instance + * @return the transformed {@code MaybeSource} instance */ @NonNull MaybeSource apply(@NonNull Maybe upstream); diff --git a/src/main/java/io/reactivex/rxjava3/core/Notification.java b/src/main/java/io/reactivex/rxjava3/core/Notification.java index 23139896cb..7f5896209f 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Notification.java +++ b/src/main/java/io/reactivex/rxjava3/core/Notification.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,35 +14,37 @@ package io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.NotificationLite; +import java.util.Objects; /** - * Represents the reactive signal types: onNext, onError and onComplete and - * holds their parameter values (a value, a Throwable, nothing). + * Represents the reactive signal types: {@code onNext}, {@code onError} and {@code onComplete} and + * holds their parameter values (a value, a {@link Throwable}, nothing). * @param the value type */ public final class Notification { final Object value; - /** Not meant to be implemented externally. */ - private Notification(Object value) { + /** Not meant to be implemented externally. + * @param value the value to carry around in the notification, not {@code null} + */ + private Notification(@Nullable Object value) { this.value = value; } /** - * Returns true if this notification is an onComplete signal. - * @return true if this notification is an onComplete signal + * Returns true if this notification is an {@code onComplete} signal. + * @return true if this notification is an {@code onComplete} signal */ public boolean isOnComplete() { return value == null; } /** - * Returns true if this notification is an onError signal and - * {@link #getError()} returns the contained Throwable. - * @return true if this notification is an onError signal + * Returns true if this notification is an {@code onError} signal and + * {@link #getError()} returns the contained {@link Throwable}. + * @return true if this notification is an {@code onError} signal * @see #getError() */ public boolean isOnError() { @@ -50,9 +52,9 @@ public boolean isOnError() { } /** - * Returns true if this notification is an onNext signal and + * Returns true if this notification is an {@code onNext} signal and * {@link #getValue()} returns the contained value. - * @return true if this notification is an onNext signal + * @return true if this notification is an {@code onNext} signal * @see #getValue() */ public boolean isOnNext() { @@ -61,7 +63,7 @@ public boolean isOnNext() { } /** - * Returns the contained value if this notification is an onNext + * Returns the contained value if this notification is an {@code onNext} * signal, null otherwise. * @return the value contained or null * @see #isOnNext() @@ -77,9 +79,9 @@ public T getValue() { } /** - * Returns the container Throwable error if this notification is an onError + * Returns the container {@link Throwable} error if this notification is an {@code onError} * signal, null otherwise. - * @return the Throwable error contained or null + * @return the {@code Throwable} error contained or {@code null} * @see #isOnError() */ @Nullable @@ -95,7 +97,7 @@ public Throwable getError() { public boolean equals(Object obj) { if (obj instanceof Notification) { Notification n = (Notification) obj; - return ObjectHelper.equals(value, n.value); + return Objects.equals(value, n.value); } return false; } @@ -121,14 +123,14 @@ public String toString() { /** * Constructs an onNext notification containing the given value. * @param the value type - * @param value the value to carry around in the notification, not null + * @param value the value to carry around in the notification, not {@code null} * @return the new Notification instance - * @throws NullPointerException if value is null + * @throws NullPointerException if value is {@code null} */ @NonNull - public static Notification createOnNext(@NonNull T value) { - ObjectHelper.requireNonNull(value, "value is null"); - return new Notification(value); + public static <@NonNull T> Notification createOnNext(T value) { + Objects.requireNonNull(value, "value is null"); + return new Notification<>(value); } /** @@ -136,19 +138,19 @@ public static Notification createOnNext(@NonNull T value) { * @param the value type * @param error the error Throwable to carry around in the notification, not null * @return the new Notification instance - * @throws NullPointerException if error is null + * @throws NullPointerException if error is {@code null} */ @NonNull public static Notification createOnError(@NonNull Throwable error) { - ObjectHelper.requireNonNull(error, "error is null"); - return new Notification(NotificationLite.error(error)); + Objects.requireNonNull(error, "error is null"); + return new Notification<>(NotificationLite.error(error)); } /** * Returns the empty and stateless shared instance of a notification representing - * an onComplete signal. + * an {@code onComplete} signal. * @param the target value type - * @return the shared Notification instance representing an onComplete signal + * @return the shared Notification instance representing an {@code onComplete} signal */ @SuppressWarnings("unchecked") @NonNull @@ -157,5 +159,5 @@ public static Notification createOnComplete() { } /** The singleton instance for createOnComplete. */ - static final Notification COMPLETE = new Notification(null); + static final Notification COMPLETE = new Notification<>(null); } diff --git a/src/main/java/io/reactivex/rxjava3/core/Observable.java b/src/main/java/io/reactivex/rxjava3/core/Observable.java index 505513c160..fcf809cdf6 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observable.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,40 +15,44 @@ import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.internal.jdk8.*; import io.reactivex.rxjava3.internal.observers.*; import io.reactivex.rxjava3.internal.operators.flowable.*; +import io.reactivex.rxjava3.internal.operators.maybe.MaybeToObservable; import io.reactivex.rxjava3.internal.operators.mixed.*; import io.reactivex.rxjava3.internal.operators.observable.*; +import io.reactivex.rxjava3.internal.operators.single.SingleToObservable; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.observables.*; import io.reactivex.rxjava3.observers.*; +import io.reactivex.rxjava3.operators.ScalarSupplier; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; /** - * The Observable class is the non-backpressured, optionally multi-valued base reactive class that + * The {@code Observable} class is the non-backpressured, optionally multi-valued base reactive class that * offers factory methods, intermediate operators and the ability to consume synchronous * and/or asynchronous reactive dataflows. *

- * Many operators in the class accept {@code ObservableSource}(s), the base reactive interface + * Many operators in the class accept {@link ObservableSource}(s), the base reactive interface * for such non-backpressured flows, which {@code Observable} itself implements as well. *

- * The Observable's operators, by default, run with a buffer size of 128 elements (see {@link Flowable#bufferSize()}), + * The {@code Observable}'s operators, by default, run with a buffer size of 128 elements (see {@link Flowable#bufferSize()}), * that can be overridden globally via the system parameter {@code rx3.buffer-size}. Most operators, however, have * overloads that allow setting their internal buffer size explicitly. *

* The documentation for this class makes use of marble diagrams. The following legend explains these diagrams: *

- * + * *

* The design of this class was derived from the * Reactive-Streams design and specification @@ -65,7 +69,7 @@ * {@code Observer.onSubscribe}. *

* Unlike the {@code Observable} of version 1.x, {@link #subscribe(Observer)} does not allow external disposal - * of a subscription and the {@code Observer} instance is expected to expose such capability. + * of a subscription and the {@link Observer} instance is expected to expose such capability. *

Example: *


  * Disposable d = Observable.just("Hello world!")
@@ -91,62 +95,79 @@
  * 
* * @param - * the type of the items emitted by the Observable + * the type of the items emitted by the {@code Observable} * @see Flowable * @see io.reactivex.rxjava3.observers.DisposableObserver */ -public abstract class Observable implements ObservableSource { +public abstract class Observable<@NonNull T> implements ObservableSource { /** - * Mirrors the one ObservableSource in an Iterable of several ObservableSources that first either emits an item or sends + * Mirrors the one {@link ObservableSource} in an {@link Iterable} of several {@code ObservableSource}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code ObservableSource}s signal an item or terminates first, all subscriptions to the other + * {@code ObservableSource}s are disposed. *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code ObservableSource}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type * @param sources - * an Iterable of ObservableSource sources competing to react first. A subscription to each source will - * occur in the same order as in the Iterable. - * @return an Observable that emits the same sequence as whichever of the source ObservableSources first - * emitted an item or sent a termination notification + * an {@code Iterable} of {@code ObservableSource} sources competing to react first. A subscription to each source will + * occur in the same order as in the {@code Iterable}. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Amb */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable amb(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new ObservableAmb(null, sources)); + public static <@NonNull T> Observable amb(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new ObservableAmb<>(null, sources)); } /** - * Mirrors the one ObservableSource in an array of several ObservableSources that first either emits an item or sends + * Mirrors the one {@link ObservableSource} in an array of several {@code ObservableSource}s that first either emits an item or sends * a termination notification. *

- * + * + *

+ * When one of the {@code ObservableSource}s signal an item or terminates first, all subscriptions to the other + * {@code ObservableSource}s are disposed. *

*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If any of the losing {@code ObservableSource}s signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param the common element type * @param sources - * an array of ObservableSource sources competing to react first. A subscription to each source will + * an array of {@code ObservableSource} sources competing to react first. A subscription to each source will * occur in the same order as in the array. - * @return an Observable that emits the same sequence as whichever of the source ObservableSources first - * emitted an item or sent a termination notification + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Amb */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable ambArray(ObservableSource... sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T> Observable ambArray(@NonNull ObservableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); int len = sources.length; if (len == 0) { return empty(); @@ -154,7 +175,7 @@ public static Observable ambArray(ObservableSource... source if (len == 1) { return (Observable)wrap(sources[0]); } - return RxJavaPlugins.onAssembly(new ObservableAmb(sources, null)); + return RxJavaPlugins.onAssembly(new ObservableAmb<>(sources, null)); } /** @@ -164,28 +185,29 @@ public static Observable ambArray(ObservableSource... source * before the {@link Flowable} class is loaded. * @return the default 'island' size or capacity-increment hint */ + @CheckReturnValue public static int bufferSize() { return Flowable.bufferSize(); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines a collection of source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the returned {@code ObservableSource}s each time an item is received from any of the returned {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided iterable of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -196,38 +218,40 @@ public static int bufferSize() { * @param * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the returned {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest(Iterable> sources, - Function combiner) { + @NonNull + public static <@NonNull T, @NonNull R> Observable combineLatest( + @NonNull Iterable<@NonNull ? extends ObservableSource> sources, + @NonNull Function combiner) { return combineLatest(sources, combiner, bufferSize()); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an {@link Iterable} of source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the returned {@code ObservableSource}s each time an item is received from any of the returned {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided {@code Iterable} of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -238,47 +262,49 @@ public static Observable combineLatest(Iterable * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources + * the aggregation function used to combine the items emitted by the returned {@code ObservableSource}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Observable - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the expected number of row combination items to be buffered internally + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest(Iterable> sources, - Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + public static <@NonNull T, @NonNull R> Observable combineLatest( + @NonNull Iterable<@NonNull ? extends ObservableSource> sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); // the queue holds a pair of values so we need to double the capacity int s = bufferSize << 1; - return RxJavaPlugins.onAssembly(new ObservableCombineLatest(null, sources, combiner, s, false)); + return RxJavaPlugins.onAssembly(new ObservableCombineLatest<>(null, sources, combiner, s, false)); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an array of source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the {@code ObservableSource}s each time an item is received from any of the returned {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided array of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestArray} does not operate by default on a particular {@link Scheduler}.
@@ -289,38 +315,40 @@ public static Observable combineLatest(Iterable * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatestArray(ObservableSource[] sources, - Function combiner) { + @NonNull + public static <@NonNull T, @NonNull R> Observable combineLatestArray( + @NonNull ObservableSource[] sources, + @NonNull Function combiner) { return combineLatestArray(sources, combiner, bufferSize()); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an array of source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided array of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestArray} does not operate by default on a particular {@link Scheduler}.
@@ -331,42 +359,44 @@ public static Observable combineLatestArray(ObservableSource * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Observable - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the expected number of row combination items to be buffered internally + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatestArray(ObservableSource[] sources, - Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T, @NonNull R> Observable combineLatestArray( + @NonNull ObservableSource[] sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } - ObjectHelper.requireNonNull(combiner, "combiner is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); // the queue holds a pair of values so we need to double the capacity int s = bufferSize << 1; - return RxJavaPlugins.onAssembly(new ObservableCombineLatest(sources, null, combiner, s, false)); + return RxJavaPlugins.onAssembly(new ObservableCombineLatest<>(sources, null, combiner, s, false)); } /** - * Combines two source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from either of the source ObservableSources, where this + * Combines two source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from either of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -376,37 +406,38 @@ public static Observable combineLatestArray(ObservableSource the element type of the second source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - BiFunction combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull BiFunction combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines three source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines three source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -417,41 +448,42 @@ public static Observable combineLatest( * @param the element type of the third source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, - Function3 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, + @NonNull Function3 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines four source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines four source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -463,44 +495,46 @@ public static Observable combineLatest( * @param the element type of the fourth source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param source4 - * the fourth source ObservableSource + * the fourth source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4, - Function4 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull Function4 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3, source4 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines five source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines five source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -513,48 +547,50 @@ public static Observable combineLatest( * @param the element type of the fifth source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param source4 - * the fourth source ObservableSource + * the fourth source {@code ObservableSource} * @param source5 - * the fifth source ObservableSource + * the fifth source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4, - ObservableSource source5, - Function5 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull ObservableSource source5, + @NonNull Function5 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3, source4, source5 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines six source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines six source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -568,51 +604,53 @@ public static Observable combineLatest( * @param the element type of the sixth source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param source4 - * the fourth source ObservableSource + * the fourth source {@code ObservableSource} * @param source5 - * the fifth source ObservableSource + * the fifth source {@code ObservableSource} * @param source6 - * the sixth source ObservableSource + * the sixth source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4, - ObservableSource source5, ObservableSource source6, - Function6 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull Function6 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3, source4, source5, source6 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines seven source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines seven source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -627,55 +665,58 @@ public static Observable combineLatest( * @param the element type of the seventh source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param source4 - * the fourth source ObservableSource + * the fourth source {@code ObservableSource} * @param source5 - * the fifth source ObservableSource + * the fifth source {@code ObservableSource} * @param source6 - * the sixth source ObservableSource + * the sixth source {@code ObservableSource} * @param source7 - * the seventh source ObservableSource + * the seventh source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4, - ObservableSource source5, ObservableSource source6, - ObservableSource source7, - Function7 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull ObservableSource source7, + @NonNull Function7 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3, source4, source5, source6, source7 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines eight source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines eight source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -691,58 +732,61 @@ public static Observable combineLatest( * @param the element type of the eighth source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param source4 - * the fourth source ObservableSource + * the fourth source {@code ObservableSource} * @param source5 - * the fifth source ObservableSource + * the fifth source {@code ObservableSource} * @param source6 - * the sixth source ObservableSource + * the sixth source {@code ObservableSource} * @param source7 - * the seventh source ObservableSource + * the seventh source {@code ObservableSource} * @param source8 - * the eighth source ObservableSource + * the eighth source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4, - ObservableSource source5, ObservableSource source6, - ObservableSource source7, ObservableSource source8, - Function8 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull ObservableSource source7, @NonNull ObservableSource source8, + @NonNull Function8 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3, source4, source5, source6, source7, source8 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines nine source ObservableSources by emitting an item that aggregates the latest values of each of the - * source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines nine source {@link ObservableSource}s by emitting an item that aggregates the latest values of each of the + * {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * + * *

*
Scheduler:
*
{@code combineLatest} does not operate by default on a particular {@link Scheduler}.
@@ -759,73 +803,76 @@ public static Observable combineLatest( * @param the element type of the ninth source * @param the combined output type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * the second source ObservableSource + * the second source {@code ObservableSource} * @param source3 - * the third source ObservableSource + * the third source {@code ObservableSource} * @param source4 - * the fourth source ObservableSource + * the fourth source {@code ObservableSource} * @param source5 - * the fifth source ObservableSource + * the fifth source {@code ObservableSource} * @param source6 - * the sixth source ObservableSource + * the sixth source {@code ObservableSource} * @param source7 - * the seventh source ObservableSource + * the seventh source {@code ObservableSource} * @param source8 - * the eighth source ObservableSource + * the eighth source {@code ObservableSource} * @param source9 - * the ninth source ObservableSource + * the ninth source {@code ObservableSource} * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8}, {@code source9} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatest( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4, - ObservableSource source5, ObservableSource source6, - ObservableSource source7, ObservableSource source8, - ObservableSource source9, - Function9 combiner) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); - ObjectHelper.requireNonNull(source9, "source9 is null"); + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> Observable combineLatest( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull ObservableSource source7, @NonNull ObservableSource source8, + @NonNull ObservableSource source9, + @NonNull Function9 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(combiner, "combiner is null"); return combineLatestArray(new ObservableSource[] { source1, source2, source3, source4, source5, source6, source7, source8, source9 }, Functions.toFunction(combiner), bufferSize()); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an array of {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function. *

- * + * *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided array of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

*
Scheduler:
- *
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
{@code combineLatestArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param @@ -833,42 +880,44 @@ public static Observable combineLates * @param * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatestDelayError(ObservableSource[] sources, - Function combiner) { - return combineLatestDelayError(sources, combiner, bufferSize()); + @NonNull + public static <@NonNull T, @NonNull R> Observable combineLatestArrayDelayError( + @NonNull ObservableSource[] sources, + @NonNull Function combiner) { + return combineLatestArrayDelayError(sources, combiner, bufferSize()); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an array of {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function and delays any error from the sources until - * all source ObservableSources terminate. + * all source {@code ObservableSource}s terminate. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided array of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided array of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
- *
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
{@code combineLatestArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param @@ -876,49 +925,51 @@ public static Observable combineLatestDelayError(ObservableSource * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Observable - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the expected number of row combination items to be buffered internally + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatestDelayError(ObservableSource[] sources, - Function combiner, int bufferSize) { + public static <@NonNull T, @NonNull R> Observable combineLatestArrayDelayError(@NonNull ObservableSource[] sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); if (sources.length == 0) { return empty(); } // the queue holds a pair of values so we need to double the capacity int s = bufferSize << 1; - return RxJavaPlugins.onAssembly(new ObservableCombineLatest(sources, null, combiner, s, true)); + return RxJavaPlugins.onAssembly(new ObservableCombineLatest<>(sources, null, combiner, s, true)); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an {@link Iterable} of {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function and delays any error from the sources until - * all source ObservableSources terminate. + * all source {@code ObservableSource}s terminate. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided iterable of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -929,39 +980,40 @@ public static Observable combineLatestDelayError(ObservableSource * the result type * @param sources - * the collection of source ObservableSources + * the {@code Iterable} of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatestDelayError(Iterable> sources, - Function combiner) { + @NonNull + public static <@NonNull T, @NonNull R> Observable combineLatestDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, + @NonNull Function combiner) { return combineLatestDelayError(sources, combiner, bufferSize()); } /** - * Combines a collection of source ObservableSources by emitting an item that aggregates the latest values of each of - * the source ObservableSources each time an item is received from any of the source ObservableSources, where this + * Combines an {@link Iterable} of {@link ObservableSource}s by emitting an item that aggregates the latest values of each of + * the {@code ObservableSource}s each time an item is received from any of the {@code ObservableSource}s, where this * aggregation is defined by a specified function and delays any error from the sources until - * all source ObservableSources terminate. + * all source {@code ObservableSource}s terminate. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

* If any of the sources never produces an item but only terminates (normally or with an error), the * resulting sequence terminates immediately (normally or with all the errors accumulated till that point). * If that input source is also synchronous, other sources after it will not be subscribed to. *

- * If the provided iterable of ObservableSources is empty, the resulting sequence completes immediately without emitting + * If the provided iterable of {@code ObservableSource}s is empty, the resulting sequence completes immediately without emitting * any items and without any calls to the combiner function. * *

- * + * *

*
Scheduler:
*
{@code combineLatestDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -972,56 +1024,58 @@ public static Observable combineLatestDelayError(Iterable * the result type * @param sources - * the collection of source ObservableSources + * the collection of source {@code ObservableSource}s * @param combiner - * the aggregation function used to combine the items emitted by the source ObservableSources + * the aggregation function used to combine the items emitted by the {@code ObservableSource}s * @param bufferSize - * the internal buffer size and prefetch amount applied to every source Observable - * @return an Observable that emits items that are the result of combining the items emitted by the source - * ObservableSources by means of the given aggregation function + * the expected number of row combination items to be buffered internally + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable combineLatestDelayError(Iterable> sources, - Function combiner, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + public static <@NonNull T, @NonNull R> Observable combineLatestDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, + @NonNull Function combiner, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(combiner, "combiner is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); // the queue holds a pair of values so we need to double the capacity int s = bufferSize << 1; - return RxJavaPlugins.onAssembly(new ObservableCombineLatest(null, sources, combiner, s, true)); + return RxJavaPlugins.onAssembly(new ObservableCombineLatest<>(null, sources, combiner, s, true)); } /** - * Concatenates elements of each ObservableSource provided via an Iterable sequence into a single sequence + * Concatenates elements of each {@link ObservableSource} provided via an {@link Iterable} sequence into a single sequence * of elements without interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the common value type of the sources - * @param sources the Iterable sequence of ObservableSources - * @return the new Observable instance + * @param sources the {@code Iterable} sequence of {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concat(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Observable concat(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return fromIterable(sources).concatMapDelayError((Function)Functions.identity(), false, bufferSize()); } /** - * Returns an Observable that emits the items emitted by each of the ObservableSources emitted by the source - * ObservableSource, one after the other, without interleaving them. + * Returns an {@code Observable} that emits the items emitted by each of the {@link ObservableSource}s emitted by the + * {@code ObservableSource}, one after the other, without interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1029,22 +1083,23 @@ public static Observable concat(Iterable the common element base type * @param sources - * an ObservableSource that emits ObservableSources - * @return an Observable that emits items all of the items emitted by the ObservableSources emitted by - * {@code ObservableSources}, one after the other, without interleaving them + * an {@code ObservableSource} that emits {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Concat */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concat(ObservableSource> sources) { + @NonNull + public static <@NonNull T> Observable concat(@NonNull ObservableSource> sources) { return concat(sources, bufferSize()); } /** - * Returns an Observable that emits the items emitted by each of the ObservableSources emitted by the source - * ObservableSource, one after the other, without interleaving them. + * Returns an {@code Observable} that emits the items emitted by each of the {@link ObservableSource}s emitted by the outer + * {@code ObservableSource}, one after the other, without interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1052,28 +1107,29 @@ public static Observable concat(ObservableSource the common element base type * @param sources - * an ObservableSource that emits ObservableSources - * @param prefetch - * the number of ObservableSources to prefetch from the sources sequence. - * @return an Observable that emits items all of the items emitted by the ObservableSources emitted by - * {@code ObservableSources}, one after the other, without interleaving them + * an {@code ObservableSource} that emits {@code ObservableSource}s + * @param bufferSize + * the number of inner {@code ObservableSource}s expected to be buffered. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Concat */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concat(ObservableSource> sources, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMap(sources, Functions.identity(), prefetch, ErrorMode.IMMEDIATE)); + public static <@NonNull T> Observable concat(@NonNull ObservableSource> sources, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMap(sources, Functions.identity(), bufferSize, ErrorMode.IMMEDIATE)); } /** - * Returns an Observable that emits the items emitted by two ObservableSources, one after the other, without + * Returns an {@code Observable} that emits the items emitted by two {@link ObservableSource}s, one after the other, without * interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1081,28 +1137,27 @@ public static Observable concat(ObservableSource the common element base type * @param source1 - * an ObservableSource to be concatenated + * an {@code ObservableSource} to be concatenated * @param source2 - * an ObservableSource to be concatenated - * @return an Observable that emits items emitted by the two source ObservableSources, one after the other, - * without interleaving them + * an {@code ObservableSource} to be concatenated + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Concat */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concat(ObservableSource source1, ObservableSource source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + public static <@NonNull T> Observable concat(@NonNull ObservableSource source1, ObservableSource source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return concatArray(source1, source2); } /** - * Returns an Observable that emits the items emitted by three ObservableSources, one after the other, without + * Returns an {@code Observable} that emits the items emitted by three {@link ObservableSource}s, one after the other, without * interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1110,33 +1165,32 @@ public static Observable concat(ObservableSource source1, Ob * * @param the common element base type * @param source1 - * an ObservableSource to be concatenated + * an {@code ObservableSource} to be concatenated * @param source2 - * an ObservableSource to be concatenated + * an {@code ObservableSource} to be concatenated * @param source3 - * an ObservableSource to be concatenated - * @return an Observable that emits items emitted by the three source ObservableSources, one after the other, - * without interleaving them + * an {@code ObservableSource} to be concatenated + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Concat */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concat( - ObservableSource source1, ObservableSource source2, - ObservableSource source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + public static <@NonNull T> Observable concat( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return concatArray(source1, source2, source3); } /** - * Returns an Observable that emits the items emitted by four ObservableSources, one after the other, without + * Returns an {@code Observable} that emits the items emitted by four {@link ObservableSource}s, one after the other, without * interleaving them. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
@@ -1144,50 +1198,52 @@ public static Observable concat( * * @param the common element base type * @param source1 - * an ObservableSource to be concatenated + * an {@code ObservableSource} to be concatenated * @param source2 - * an ObservableSource to be concatenated + * an {@code ObservableSource} to be concatenated * @param source3 - * an ObservableSource to be concatenated + * an {@code ObservableSource} to be concatenated * @param source4 - * an ObservableSource to be concatenated - * @return an Observable that emits items emitted by the four source ObservableSources, one after the other, - * without interleaving them + * an {@code ObservableSource} to be concatenated + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Concat */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concat( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + public static <@NonNull T> Observable concat( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return concatArray(source1, source2, source3, source4); } /** - * Concatenates a variable number of ObservableSource sources. + * Concatenates a variable number of {@link ObservableSource} sources. *

- * Note: named this way because of overload conflict with concat(ObservableSource<ObservableSource>) + * Note: named this way because of overload conflict with {@code concat(ObservableSource)} *

- * + * *

*
Scheduler:
*
{@code concatArray} does not operate by default on a particular {@link Scheduler}.
*
* @param sources the array of sources * @param the common base value type - * @return the new Observable instance - * @throws NullPointerException if sources is null + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatArray(ObservableSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Observable concatArray(@NonNull ObservableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } @@ -1198,80 +1254,91 @@ public static Observable concatArray(ObservableSource... sou } /** - * Concatenates a variable number of ObservableSource sources and delays errors from any of them + * Concatenates a variable number of {@link ObservableSource} sources and delays errors from any of them * till all terminate. *

- * + * *

*
Scheduler:
*
{@code concatArrayDelayError} does not operate by default on a particular {@link Scheduler}.
*
* @param sources the array of sources * @param the common base value type - * @return the new Observable instance - * @throws NullPointerException if sources is null + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} */ - @SuppressWarnings({ "unchecked" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatArrayDelayError(ObservableSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Observable concatArrayDelayError(@NonNull ObservableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } if (sources.length == 1) { - return (Observable)wrap(sources[0]); + @SuppressWarnings("unchecked") + Observable source = (Observable)wrap(sources[0]); + return source; } return concatDelayError(fromArray(sources)); } /** - * Concatenates an array of ObservableSources eagerly into a single stream of values. + * Concatenates an array of {@link ObservableSource}s eagerly into a single stream of values. *

- * + * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them * in order, each one after the previous one completes. *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources an array of ObservableSources that need to be eagerly concatenated - * @return the new ObservableSource instance with the specified concatenation behavior + * @param sources an array of {@code ObservableSource}s that need to be eagerly concatenated + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatArrayEager(ObservableSource... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Observable concatArrayEager(@NonNull ObservableSource... sources) { return concatArrayEager(bufferSize(), bufferSize(), sources); } /** - * Concatenates an array of ObservableSources eagerly into a single stream of values. + * Concatenates an array of {@link ObservableSource}s eagerly into a single stream of values. *

* *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them * in order, each one after the previous one completes. *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources an array of ObservableSources that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrent subscriptions at a time, Integer.MAX_VALUE + * @param sources an array of {@code ObservableSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrent subscriptions at a time, {@link Integer#MAX_VALUE} * is interpreted as indication to subscribe to all sources at once - * @param prefetch the number of elements to prefetch from each ObservableSource source - * @return the new ObservableSource instance with the specified concatenation behavior + * @param bufferSize the number of elements expected from each {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @since 2.0 */ @SuppressWarnings({ "rawtypes", "unchecked" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatArrayEager(int maxConcurrency, int prefetch, ObservableSource... sources) { - return fromArray(sources).concatMapEagerDelayError((Function)Functions.identity(), false, maxConcurrency, prefetch); + @NonNull + @SafeVarargs + public static <@NonNull T> Observable concatArrayEager(int maxConcurrency, int bufferSize, @NonNull ObservableSource... sources) { + return fromArray(sources).concatMapEagerDelayError((Function)Functions.identity(), false, maxConcurrency, bufferSize); } /** @@ -1281,7 +1348,7 @@ public static Observable concatArrayEager(int maxConcurrency, int prefetc * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s * and then drains them in order, each one after the previous one completes. *

*
Scheduler:
@@ -1289,12 +1356,15 @@ public static Observable concatArrayEager(int maxConcurrency, int prefetc *
* @param the value type * @param sources an array of {@code ObservableSource}s that need to be eagerly concatenated - * @return the new Observable instance with the specified concatenation behavior + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.2.1 - experimental */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatArrayEagerDelayError(ObservableSource... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Observable concatArrayEagerDelayError(@NonNull ObservableSource... sources) { return concatArrayEagerDelayError(bufferSize(), bufferSize(), sources); } @@ -1305,7 +1375,7 @@ public static Observable concatArrayEagerDelayError(ObservableSource *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s * and then drains them in order, each one after the previous one completes. *

*
Scheduler:
@@ -1313,190 +1383,329 @@ public static Observable concatArrayEagerDelayError(ObservableSource * @param the value type * @param sources an array of {@code ObservableSource}s that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrent subscriptions at a time, Integer.MAX_VALUE + * @param maxConcurrency the maximum number of concurrent subscriptions at a time, {@link Integer#MAX_VALUE} * is interpreted as indication to subscribe to all sources at once - * @param prefetch the number of elements to prefetch from each {@code ObservableSource} source - * @return the new Observable instance with the specified concatenation behavior + * @param bufferSize the number of elements expected from each {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @since 2.2.1 - experimental */ @SuppressWarnings({ "rawtypes", "unchecked" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatArrayEagerDelayError(int maxConcurrency, int prefetch, ObservableSource... sources) { - return fromArray(sources).concatMapEagerDelayError((Function)Functions.identity(), true, maxConcurrency, prefetch); + @NonNull + @SafeVarargs + public static <@NonNull T> Observable concatArrayEagerDelayError(int maxConcurrency, int bufferSize, @NonNull ObservableSource... sources) { + return fromArray(sources).concatMapEagerDelayError((Function)Functions.identity(), true, maxConcurrency, bufferSize); } /** - * Concatenates the Iterable sequence of ObservableSources into a single sequence by subscribing to each ObservableSource, - * one after the other, one at a time and delays any errors till the all inner ObservableSources terminate. + * Concatenates the {@link Iterable} sequence of {@link ObservableSource}s into a single {@code Observable} sequence + * by subscribing to each {@code ObservableSource}, one after the other, one at a time and delays any errors till + * the all inner {@code ObservableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type - * @param sources the Iterable sequence of ObservableSources - * @return the new ObservableSource with the concatenating behavior + * @param sources the {@code Iterable} sequence of {@code ObservableSource}s + * @return the new {@code Observable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatDelayError(Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Observable concatDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return concatDelayError(fromIterable(sources)); } /** - * Concatenates the ObservableSource sequence of ObservableSources into a single sequence by subscribing to each inner ObservableSource, - * one after the other, one at a time and delays any errors till the all inner and the outer ObservableSources terminate. + * Concatenates the {@link ObservableSource} sequence of {@code ObservableSource}s into a single {@code Observable} sequence + * by subscribing to each inner {@code ObservableSource}, one after the other, one at a time and delays any errors till the + * all inner and the outer {@code ObservableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type - * @param sources the ObservableSource sequence of ObservableSources - * @return the new ObservableSource with the concatenating behavior + * @param sources the {@code ObservableSource} sequence of {@code ObservableSource}s + * @return the new {@code Observable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatDelayError(ObservableSource> sources) { + @NonNull + public static <@NonNull T> Observable concatDelayError(@NonNull ObservableSource> sources) { return concatDelayError(sources, bufferSize(), true); } /** - * Concatenates the ObservableSource sequence of ObservableSources into a single sequence by subscribing to each inner ObservableSource, - * one after the other, one at a time and delays any errors till the all inner and the outer ObservableSources terminate. + * Concatenates the {@link ObservableSource} sequence of {@code ObservableSource}s into a single sequence by subscribing to each inner {@code ObservableSource}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code ObservableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the common element base type - * @param sources the ObservableSource sequence of ObservableSources - * @param prefetch the number of elements to prefetch from the outer ObservableSource - * @param tillTheEnd if true exceptions from the outer and all inner ObservableSources are delayed to the end - * if false, exception from the outer ObservableSource is delayed till the current ObservableSource terminates - * @return the new ObservableSource with the concatenating behavior + * @param sources the {@code ObservableSource} sequence of {@code ObservableSource}s + * @param bufferSize the number of inner {@code ObservableSource}s expected to be buffered + * @param tillTheEnd if {@code true}, exceptions from the outer and all inner {@code ObservableSource}s are delayed to the end + * if {@code false}, exception from the outer {@code ObservableSource} is delayed till the active {@code ObservableSource} terminates + * @return the new {@code Observable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive */ @SuppressWarnings({ "rawtypes", "unchecked" }) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatDelayError(ObservableSource> sources, int prefetch, boolean tillTheEnd) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatMap(sources, Functions.identity(), prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); + public static <@NonNull T> Observable concatDelayError(@NonNull ObservableSource> sources, int bufferSize, boolean tillTheEnd) { + Objects.requireNonNull(sources, "sources is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatMap(sources, Functions.identity(), bufferSize, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); } /** - * Concatenates an ObservableSource sequence of ObservableSources eagerly into a single stream of values. + * Concatenates a sequence of {@link ObservableSource}s eagerly into a single stream of values. *

- * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * emitted source ObservableSources as they are observed. The operator buffers the values emitted by these - * ObservableSources and then drains them in order, each one after the previous one completes. + * *

- * + * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them + * in order, each one after the previous one completes. *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of ObservableSources that need to be eagerly concatenated - * @return the new ObservableSource instance with the specified concatenation behavior + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatEager(ObservableSource> sources) { + @NonNull + public static <@NonNull T> Observable concatEager(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { return concatEager(sources, bufferSize(), bufferSize()); } /** - * Concatenates an ObservableSource sequence of ObservableSources eagerly into a single stream of values. + * Concatenates a sequence of {@link ObservableSource}s eagerly into a single stream of values and + * runs a limited number of inner sequences at once. *

- * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * emitted source ObservableSources as they are observed. The operator buffers the values emitted by these - * ObservableSources and then drains them in order, each one after the previous one completes. + * *

- * + * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them + * in order, each one after the previous one completes. *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of ObservableSources that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrently running inner ObservableSources; Integer.MAX_VALUE - * is interpreted as all inner ObservableSources can be active at the same time - * @param prefetch the number of elements to prefetch from each inner ObservableSource source - * @return the new ObservableSource instance with the specified concatenation behavior + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code ObservableSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code ObservableSource}s can be active at the same time + * @param bufferSize the number of elements expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @since 2.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatEager(ObservableSource> sources, int maxConcurrency, int prefetch) { - return wrap(sources).concatMapEager((Function)Functions.identity(), maxConcurrency, prefetch); + @NonNull + public static <@NonNull T> Observable concatEager(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, int maxConcurrency, int bufferSize) { + return fromIterable(sources).concatMapEagerDelayError((Function)Functions.identity(), false, maxConcurrency, bufferSize); } /** - * Concatenates a sequence of ObservableSources eagerly into a single stream of values. + * Concatenates an {@link ObservableSource} sequence of {@code ObservableSource}s eagerly into a single stream of values. *

- * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them - * in order, each one after the previous one completes. + * *

- * + * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code ObservableSource}s as they are observed. The operator buffers the values emitted by these + * {@code ObservableSource}s and then drains them in order, each one after the previous one completes. *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of ObservableSources that need to be eagerly concatenated - * @return the new ObservableSource instance with the specified concatenation behavior + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatEager(Iterable> sources) { + @NonNull + public static <@NonNull T> Observable concatEager(@NonNull ObservableSource> sources) { return concatEager(sources, bufferSize(), bufferSize()); } /** - * Concatenates a sequence of ObservableSources eagerly into a single stream of values. + * Concatenates an {@link ObservableSource} sequence of {@code ObservableSource}s eagerly into a single stream of values + * and runs a limited number of inner sequences at once. + * + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code ObservableSource}s as they are observed. The operator buffers the values emitted by these + * {@code ObservableSource}s and then drains them in order, each one after the previous one completes. + *

+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code ObservableSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code ObservableSource}s can be active at the same time + * @param bufferSize the number of inner {@code ObservableSource} expected to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive + * @since 2.0 + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable concatEager(@NonNull ObservableSource> sources, int maxConcurrency, int bufferSize) { + return wrap(sources).concatMapEager((Function)Functions.identity(), maxConcurrency, bufferSize); + } + + /** + * Concatenates a sequence of {@link ObservableSource}s eagerly into a single stream of values, + * delaying errors until all the inner sequences terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them + * in order, each one after the previous one completes. + *

+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { + return concatEagerDelayError(sources, bufferSize(), bufferSize()); + } + + /** + * Concatenates a sequence of {@link ObservableSource}s eagerly into a single stream of values, + * delaying errors until all the inner sequences terminate and runs a limited number of inner + * sequences at once. + *

+ * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them + * {@code ObservableSource}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them * in order, each one after the previous one completes. + *

+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code ObservableSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code ObservableSource}s can be active at the same time + * @param bufferSize the number of elements expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive + * @since 3.0.0 + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, int maxConcurrency, int bufferSize) { + return fromIterable(sources).concatMapEagerDelayError((Function)Functions.identity(), true, maxConcurrency, bufferSize); + } + + /** + * Concatenates an {@link ObservableSource} sequence of {@code ObservableSource}s eagerly into a single stream of values, + * delaying errors until all the inner and the outer sequence terminate. + *

+ * *

- * + * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code ObservableSource}s as they are observed. The operator buffers the values emitted by these + * {@code ObservableSource}s and then drains them in order, each one after the previous one completes. *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of ObservableSources that need to be eagerly concatenated - * @param maxConcurrency the maximum number of concurrently running inner ObservableSources; Integer.MAX_VALUE - * is interpreted as all inner ObservableSources can be active at the same time - * @param prefetch the number of elements to prefetch from each inner ObservableSource source - * @return the new ObservableSource instance with the specified concatenation behavior - * @since 2.0 + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable concatEagerDelayError(@NonNull ObservableSource> sources) { + return concatEagerDelayError(sources, bufferSize(), bufferSize()); + } + + /** + * Concatenates an {@link ObservableSource} sequence of {@code ObservableSource}s eagerly into a single stream of values, + * delaying errors until all the inner and the outer sequence terminate and runs a limited number of inner sequences at once. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code ObservableSource}s as they are observed. The operator buffers the values emitted by these + * {@code ObservableSource}s and then drains them in order, each one after the previous one completes. + *

+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code ObservableSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code ObservableSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code ObservableSource}s can be active at the same time + * @param bufferSize the number of inner {@code ObservableSource} expected to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive + * @since 3.0.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable concatEager(Iterable> sources, int maxConcurrency, int prefetch) { - return fromIterable(sources).concatMapEagerDelayError((Function)Functions.identity(), false, maxConcurrency, prefetch); + @NonNull + public static <@NonNull T> Observable concatEagerDelayError(@NonNull ObservableSource> sources, int maxConcurrency, int bufferSize) { + return wrap(sources).concatMapEagerDelayError((Function)Functions.identity(), true, maxConcurrency, bufferSize); } /** - * Provides an API (via a cold Observable) that bridges the reactive world with the callback-style world. + * Provides an API (via a cold {@code Observable}) that bridges the reactive world with the callback-style world. *

* Example: *


@@ -1526,12 +1735,12 @@ public static  Observable concatEager(Iterable
-     * 
+     * 
      * 

- * You should call the ObservableEmitter's onNext, onError and onComplete methods in a serialized fashion. The + * You should call the {@code ObservableEmitter}'s {@code onNext}, {@code onError} and {@code onComplete} methods in a serialized fashion. The * rest of its methods are thread-safe. *

*
Scheduler:
@@ -1539,8 +1748,9 @@ public static Observable concatEager(Iterable * * @param the element type - * @param source the emitter that is called when an Observer subscribes to the returned {@code Observable} - * @return the new Observable instance + * @param source the emitter that is called when an {@code Observer} subscribes to the returned {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source} is {@code null} * @see ObservableOnSubscribe * @see ObservableEmitter * @see Cancellable @@ -1548,20 +1758,20 @@ public static Observable concatEager(Iterable Observable create(ObservableOnSubscribe source) { - ObjectHelper.requireNonNull(source, "source is null"); - return RxJavaPlugins.onAssembly(new ObservableCreate(source)); + public static <@NonNull T> Observable create(@NonNull ObservableOnSubscribe source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new ObservableCreate<>(source)); } /** - * Returns an Observable that calls an ObservableSource factory to create an ObservableSource for each new Observer - * that subscribes. That is, for each subscriber, the actual ObservableSource that subscriber observes is + * Returns an {@code Observable} that calls an {@link ObservableSource} factory to create an {@code ObservableSource} for each new {@link Observer} + * that subscribes. That is, for each subscriber, the actual {@code ObservableSource} that subscriber observes is * determined by the factory function. *

- * + * *

- * The defer Observer allows you to defer or delay emitting items from an ObservableSource until such time as an - * Observer subscribes to the ObservableSource. This allows an {@link Observer} to easily obtain updates or a + * The {@code defer} operator allows you to defer or delay emitting items from an {@code ObservableSource} until such time as an + * {@code Observer} subscribes to the {@code ObservableSource}. This allows an {@code Observer} to easily obtain updates or a * refreshed version of the sequence. *

*
Scheduler:
@@ -1569,101 +1779,131 @@ public static Observable create(ObservableOnSubscribe source) { *
* * @param supplier - * the ObservableSource factory function to invoke for each {@link Observer} that subscribes to the - * resulting ObservableSource + * the {@code ObservableSource} factory function to invoke for each {@code Observer} that subscribes to the + * resulting {@code Observable} * @param - * the type of the items emitted by the ObservableSource - * @return an Observable whose {@link Observer}s' subscriptions trigger an invocation of the given - * ObservableSource factory function + * the type of the items emitted by the {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see ReactiveX operators documentation: Defer */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable defer(Supplier> supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new ObservableDefer(supplier)); + public static <@NonNull T> Observable defer(@NonNull Supplier> supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new ObservableDefer<>(supplier)); } /** - * Returns an Observable that emits no items to the {@link Observer} and immediately invokes its + * Returns an {@code Observable} that emits no items to the {@link Observer} and immediately invokes its * {@link Observer#onComplete onComplete} method. *

- * + * *

*
Scheduler:
*
{@code empty} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of the items (ostensibly) emitted by the ObservableSource - * @return an Observable that emits no items to the {@link Observer} but immediately invokes the - * {@link Observer}'s {@link Observer#onComplete() onComplete} method + * the type of the items (ostensibly) emitted by the {@code Observable} + * @return the shared {@code Observable} instance * @see ReactiveX operators documentation: Empty */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Observable empty() { + @NonNull + public static <@NonNull T> Observable empty() { return RxJavaPlugins.onAssembly((Observable) ObservableEmpty.INSTANCE); } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the - * Observer subscribes to it. + * Returns an {@code Observable} that invokes an {@link Observer}'s {@link Observer#onError onError} method when the + * {@code Observer} subscribes to it. *

- * + * *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
* - * @param errorSupplier - * a Supplier factory to return a Throwable for each individual Observer + * @param supplier + * a {@link Supplier} factory to return a {@link Throwable} for each individual {@code Observer} * @param - * the type of the items (ostensibly) emitted by the ObservableSource - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when - * the Observer subscribes to it + * the type of the items (ostensibly) emitted by the {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable error(Supplier errorSupplier) { - ObjectHelper.requireNonNull(errorSupplier, "errorSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableError(errorSupplier)); + public static <@NonNull T> Observable error(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new ObservableError<>(supplier)); } /** - * Returns an Observable that invokes an {@link Observer}'s {@link Observer#onError onError} method when the - * Observer subscribes to it. + * Returns an {@code Observable} that invokes an {@link Observer}'s {@link Observer#onError onError} method when the + * {@code Observer} subscribes to it. *

- * + * *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
* - * @param exception - * the particular Throwable to pass to {@link Observer#onError onError} + * @param throwable + * the particular {@link Throwable} to pass to {@link Observer#onError onError} * @param - * the type of the items (ostensibly) emitted by the ObservableSource - * @return an Observable that invokes the {@link Observer}'s {@link Observer#onError onError} method when - * the Observer subscribes to it + * the type of the items (ostensibly) emitted by the {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code throwable} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable error(final Throwable exception) { - ObjectHelper.requireNonNull(exception, "exception is null"); - return error(Functions.justSupplier(exception)); + public static <@NonNull T> Observable error(@NonNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is null"); + return error(Functions.justSupplier(throwable)); + } + + /** + * Returns an {@code Observable} instance that runs the given {@link Action} for each {@link Observer} and + * emits either its exception or simply completes. + *

+ * + *

+ *
Scheduler:
+ *
{@code fromAction} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the {@code Action} throws an exception, the respective {@link Throwable} is + * delivered to the downstream via {@link Observer#onError(Throwable)}, + * except when the downstream has canceled the resulting {@code Observable} source. + * In this latter case, the {@code Throwable} is delivered to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + *
+ *
+ * @param the target type + * @param action the {@code Action} to run for each {@code Observer} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code action} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Observable fromAction(@NonNull Action action) { + Objects.requireNonNull(action, "action is null"); + return RxJavaPlugins.onAssembly(new ObservableFromAction<>(action)); } /** - * Converts an Array into an ObservableSource that emits the items in the Array. + * Converts an array into an {@link ObservableSource} that emits the items in the array. *

- * + * *

*
Scheduler:
*
{@code fromArray} does not operate by default on a particular {@link Scheduler}.
@@ -1672,49 +1912,52 @@ public static Observable error(final Throwable exception) { * @param items * the array of elements * @param - * the type of items in the Array and the type of items to be emitted by the resulting ObservableSource - * @return an Observable that emits each item in the source Array + * the type of items in the array and the type of items to be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code items} is {@code null} * @see ReactiveX operators documentation: From */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @NonNull - public static Observable fromArray(T... items) { - ObjectHelper.requireNonNull(items, "items is null"); + @SafeVarargs + public static <@NonNull T> Observable fromArray(@NonNull T... items) { + Objects.requireNonNull(items, "items is null"); if (items.length == 0) { return empty(); } if (items.length == 1) { return just(items[0]); } - return RxJavaPlugins.onAssembly(new ObservableFromArray(items)); + return RxJavaPlugins.onAssembly(new ObservableFromArray<>(items)); } /** - * Returns an Observable that, when an observer subscribes to it, invokes a function you specify and then + * Returns an {@code Observable} that, when an observer subscribes to it, invokes a function you specify and then * emits the value returned from that function. *

- * + * *

* This allows you to defer the execution of the function you specify until an observer subscribes to the - * ObservableSource. That is to say, it makes the function "lazy." + * {@code Observable}. That is to say, it makes the function "lazy." *

*
Scheduler:
*
{@code fromCallable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
*
If the {@link Callable} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link Observer#onError(Throwable)}, - * except when the downstream has disposed this {@code Observable} source. + * except when the downstream has disposed the current {@code Observable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + * {@link RxJavaPlugins#onError(Throwable)} as an {@link UndeliverableException}. *
*
- * @param supplier + * @param callable * a function, the execution of which should be deferred; {@code fromCallable} will invoke this - * function only when an observer subscribes to the ObservableSource that {@code fromCallable} returns + * function only when an observer subscribes to the {@code Observable} that {@code fromCallable} returns * @param - * the type of the item emitted by the ObservableSource - * @return an Observable whose {@link Observer}s' subscriptions trigger an invocation of the given function + * the type of the item returned by the {@code Callable} and emitted by the {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code callable} is {@code null} * @see #defer(Supplier) * @see #fromSupplier(Supplier) * @since 2.0 @@ -1722,200 +1965,179 @@ public static Observable fromArray(T... items) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable fromCallable(Callable supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new ObservableFromCallable(supplier)); + public static <@NonNull T> Observable fromCallable(@NonNull Callable callable) { + Objects.requireNonNull(callable, "callable is null"); + return RxJavaPlugins.onAssembly(new ObservableFromCallable<>(callable)); } /** - * Converts a {@link Future} into an ObservableSource. - *

- * - *

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

- * Important note: This ObservableSource is blocking; you cannot dispose it. - *

- * Unlike 1.x, disposing the Observable won't cancel the future. If necessary, one can use composition to achieve the - * cancellation effect: {@code futureObservableSource.doOnDispose(() -> future.cancel(true));}. + * *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromCompletable} does not operate by default on a particular {@link Scheduler}.
*
- * - * @param future - * the source {@link Future} - * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting ObservableSource - * @return an Observable that emits the item from the source {@link Future} - * @see ReactiveX operators documentation: From + * @param the target type + * @param completableSource the {@code CompletableSource} to convert from + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code completableSource} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable fromFuture(Future future) { - ObjectHelper.requireNonNull(future, "future is null"); - return RxJavaPlugins.onAssembly(new ObservableFromFuture(future, 0L, null)); + public static <@NonNull T> Observable fromCompletable(@NonNull CompletableSource completableSource) { + Objects.requireNonNull(completableSource, "completableSource is null"); + return RxJavaPlugins.onAssembly(new ObservableFromCompletable<>(completableSource)); } /** - * Converts a {@link Future} into an ObservableSource, with a timeout on the Future. + * Converts a {@link Future} into an {@code Observable}. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into an ObservableSource that emits the - * return value of the {@link Future#get} method of that object, by passing the object into the {@code from} - * method. + * The operator calls {@link Future#get()}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Unlike 1.x, disposing the Observable won't cancel the future. If necessary, one can use composition to achieve the + * Unlike 1.x, disposing the {@code Observable} won't cancel the future. If necessary, one can use composition to achieve the * cancellation effect: {@code futureObservableSource.doOnDispose(() -> future.cancel(true));}. *

- * Important note: This ObservableSource is blocking; you cannot dispose it. + * Also note that this operator will consume a {@link CompletionStage}-based {@code Future} subclass (such as + * {@link CompletableFuture}) in a blocking manner as well. Use the {@link #fromCompletionStage(CompletionStage)} + * operator to convert and consume such sources in a non-blocking fashion instead. *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} - * @param timeout - * the maximum time to wait before calling {@code get} - * @param unit - * the {@link TimeUnit} of the {@code timeout} argument + * the source {@code Future} * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting ObservableSource - * @return an Observable that emits the item from the source {@link Future} + * the type of object that the {@code Future} returns, and also the type of item to be emitted by + * the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code future} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable fromFuture(Future future, long timeout, TimeUnit unit) { - ObjectHelper.requireNonNull(future, "future is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - return RxJavaPlugins.onAssembly(new ObservableFromFuture(future, timeout, unit)); + public static <@NonNull T> Observable fromFuture(@NonNull Future future) { + Objects.requireNonNull(future, "future is null"); + return RxJavaPlugins.onAssembly(new ObservableFromFuture<>(future, 0L, null)); } /** - * Converts a {@link Future} into an ObservableSource, with a timeout on the Future. + * Converts a {@link Future} into an {@code Observable}, with a timeout on the {@code Future}. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into an ObservableSource that emits the - * return value of the {@link Future#get} method of that object, by passing the object into the {@code from} - * method. + * The operator calls {@link Future#get(long, TimeUnit)}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Unlike 1.x, disposing the Observable won't cancel the future. If necessary, one can use composition to achieve the + * Unlike 1.x, disposing the {@code Observable} won't cancel the future. If necessary, one can use composition to achieve the * cancellation effect: {@code futureObservableSource.doOnDispose(() -> future.cancel(true));}. *

- * Important note: This ObservableSource is blocking; you cannot dispose it. + * Also note that this operator will consume a {@link CompletionStage}-based {@code Future} subclass (such as + * {@link CompletableFuture}) in a blocking manner as well. Use the {@link #fromCompletionStage(CompletionStage)} + * operator to convert and consume such sources in a non-blocking fashion instead. *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param timeout * the maximum time to wait before calling {@code get} * @param unit * the {@link TimeUnit} of the {@code timeout} argument - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as - * {@link Schedulers#io()} that can block and wait on the Future * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting ObservableSource - * @return an Observable that emits the item from the source {@link Future} + * the type of object that the {@code Future} returns, and also the type of item to be emitted by + * the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code future} or {@code unit} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @NonNull - @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Observable fromFuture(Future future, long timeout, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - Observable o = fromFuture(future, timeout, unit); - return o.subscribeOn(scheduler); + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Observable fromFuture(@NonNull Future future, long timeout, @NonNull TimeUnit unit) { + Objects.requireNonNull(future, "future is null"); + Objects.requireNonNull(unit, "unit is null"); + return RxJavaPlugins.onAssembly(new ObservableFromFuture<>(future, timeout, unit)); } /** - * Converts a {@link Future}, operating on a specified {@link Scheduler}, into an ObservableSource. - *

- * - *

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

- * Unlike 1.x, disposing the Observable won't cancel the future. If necessary, one can use composition to achieve the - * cancellation effect: {@code futureObservableSource.doOnDispose(() -> future.cancel(true));}. + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
{@code fromIterable} does not operate by default on a particular {@link Scheduler}.
*
* - * @param future - * the source {@link Future} - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as - * {@link Schedulers#io()} that can block and wait on the Future + * @param source + * the source {@code Iterable} sequence * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting ObservableSource - * @return an Observable that emits the item from the source {@link Future} + * the type of items in the {@code Iterable} sequence and the type of items to be emitted by the + * resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromStream(Stream) */ @CheckReturnValue @NonNull - @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Observable fromFuture(Future future, Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - Observable o = fromFuture(future); - return o.subscribeOn(scheduler); + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Observable fromIterable(@NonNull Iterable source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new ObservableFromIterable<>(source)); } /** - * Converts an {@link Iterable} sequence into an ObservableSource that emits the items in the sequence. + * Returns an {@code Observable} instance that when subscribed to, subscribes to the {@link MaybeSource} instance and + * emits {@code onSuccess} as a single item or forwards any {@code onComplete} or + * {@code onError} signal. *

- * + * *

*
Scheduler:
- *
{@code fromIterable} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromMaybe} does not operate by default on a particular {@link Scheduler}.
*
- * - * @param source - * the source {@link Iterable} sequence - * @param - * the type of items in the {@link Iterable} sequence and the type of items to be emitted by the - * resulting ObservableSource - * @return an Observable that emits each item in the source {@link Iterable} sequence - * @see ReactiveX operators documentation: From + * @param the value type of the {@code MaybeSource} element + * @param maybe the {@code MaybeSource} instance to subscribe to, not {@code null} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code maybe} is {@code null} + * @since 3.0.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable fromIterable(Iterable source) { - ObjectHelper.requireNonNull(source, "source is null"); - return RxJavaPlugins.onAssembly(new ObservableFromIterable(source)); + public static <@NonNull T> Observable fromMaybe(@NonNull MaybeSource maybe) { + Objects.requireNonNull(maybe, "maybe is null"); + return RxJavaPlugins.onAssembly(new MaybeToObservable<>(maybe)); } /** - * Converts an arbitrary Reactive-Streams Publisher into an Observable. + * Converts an arbitrary Reactive Streams {@link Publisher} into an {@code Observable}. *

* *

- * The {@link Publisher} must follow the + * The {@code Publisher} must follow the * Reactive-Streams specification. * Violating the specification may result in undefined behavior. *

* If possible, use {@link #create(ObservableOnSubscribe)} to create a * source-like {@code Observable} instead. *

- * Note that even though {@link Publisher} appears to be a functional interface, it + * Note that even though {@code Publisher} appears to be a functional interface, it * is not recommended to implement it through a lambda as the specification requires * state management that is not achievable with a stateless lambda. *

@@ -1926,45 +2148,105 @@ public static Observable fromIterable(Iterable source) { *
{@code fromPublisher} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type of the flow - * @param publisher the Publisher to convert - * @return the new Observable instance - * @throws NullPointerException if publisher is null + * @param publisher the {@code Publisher} to convert + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code publisher} is {@code null} * @see #create(ObservableOnSubscribe) */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable fromPublisher(Publisher publisher) { - ObjectHelper.requireNonNull(publisher, "publisher is null"); - return RxJavaPlugins.onAssembly(new ObservableFromPublisher(publisher)); + public static <@NonNull T> Observable fromPublisher(@NonNull Publisher publisher) { + Objects.requireNonNull(publisher, "publisher is null"); + return RxJavaPlugins.onAssembly(new ObservableFromPublisher<>(publisher)); + } + + /** + * Returns an {@code Observable} instance that runs the given {@link Runnable} for each {@link Observer} and + * emits either its unchecked exception or simply completes. + *

+ * + *

+ * If the code to be wrapped needs to throw a checked or more broader {@link Throwable} exception, that + * exception has to be converted to an unchecked exception by the wrapped code itself. Alternatively, + * use the {@link #fromAction(Action)} method which allows the wrapped code to throw any {@code Throwable} + * exception and will signal it to observers as-is. + *

+ *
Scheduler:
+ *
{@code fromRunnable} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the {@code Runnable} throws an exception, the respective {@code Throwable} is + * delivered to the downstream via {@link Observer#onError(Throwable)}, + * except when the downstream has canceled the resulting {@code Observable} source. + * In this latter case, the {@code Throwable} is delivered to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + *
+ *
+ * @param the target type + * @param run the {@code Runnable} to run for each {@code Observer} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code run} is {@code null} + * @since 3.0.0 + * @see #fromAction(Action) + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Observable fromRunnable(@NonNull Runnable run) { + Objects.requireNonNull(run, "run is null"); + return RxJavaPlugins.onAssembly(new ObservableFromRunnable<>(run)); + } + + /** + * Returns an {@code Observable} instance that when subscribed to, subscribes to the {@link SingleSource} instance and + * emits {@code onSuccess} as a single item or forwards the {@code onError} signal. + *

+ * + *

+ *
Scheduler:
+ *
{@code fromSingle} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type of the {@code SingleSource} element + * @param source the {@code SingleSource} instance to subscribe to, not {@code null} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Observable fromSingle(@NonNull SingleSource source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new SingleToObservable<>(source)); } /** - * Returns an Observable that, when an observer subscribes to it, invokes a supplier function you specify and then + * Returns an {@code Observable} that, when an observer subscribes to it, invokes a supplier function you specify and then * emits the value returned from that function. *

- * + * *

* This allows you to defer the execution of the function you specify until an observer subscribes to the - * ObservableSource. That is to say, it makes the function "lazy." + * {@code Observable}. That is to say, it makes the function "lazy." *

*
Scheduler:
*
{@code fromSupplier} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
*
If the {@link Supplier} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link Observer#onError(Throwable)}, - * except when the downstream has disposed this {@code Observable} source. + * except when the downstream has disposed the current {@code Observable} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} as an {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}. + * {@link RxJavaPlugins#onError(Throwable)} as an {@link UndeliverableException}. *
*
* @param supplier * a function, the execution of which should be deferred; {@code fromSupplier} will invoke this - * function only when an observer subscribes to the ObservableSource that {@code fromSupplier} returns + * function only when an observer subscribes to the {@code Observable} that {@code fromSupplier} returns * @param - * the type of the item emitted by the ObservableSource - * @return an Observable whose {@link Observer}s' subscriptions trigger an invocation of the given function + * the type of the item emitted by the {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code supplier} is {@code null} * @see #defer(Supplier) * @see #fromCallable(Callable) * @since 3.0.0 @@ -1972,15 +2254,15 @@ public static Observable fromPublisher(Publisher publisher) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable fromSupplier(Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new ObservableFromSupplier(supplier)); + public static <@NonNull T> Observable fromSupplier(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new ObservableFromSupplier<>(supplier)); } /** * Returns a cold, synchronous and stateless generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -1992,25 +2274,26 @@ public static Observable fromSupplier(Supplier supplier) { *

* * @param the generated value type - * @param generator the Consumer called whenever a particular downstream Observer has - * requested a value. The callback then should call {@code onNext}, {@code onError} or - * {@code onComplete} to signal a value or a terminal event. Signalling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @return the new Observable instance + * @param generator the {@link Consumer} called in a loop after a downstream {@link Observer} has + * subscribed. The callback then should call {@code onNext}, {@code onError} or + * {@code onComplete} to signal a value or a terminal event. Signaling multiple {@code onNext} + * in a call will make the operator signal {@link IllegalStateException}. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code generator} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable generate(final Consumer> generator) { - ObjectHelper.requireNonNull(generator, "generator is null"); - return generate(Functions.nullSupplier(), - ObservableInternalHelper.simpleGenerator(generator), Functions.emptyConsumer()); + public static <@NonNull T> Observable generate(@NonNull Consumer> generator) { + Objects.requireNonNull(generator, "generator is null"); + return generate(Functions.nullSupplier(), + ObservableInternalHelper.simpleGenerator(generator), Functions.emptyConsumer()); } /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2021,27 +2304,28 @@ public static Observable generate(final Consumer> generator) { *

{@code generate} does not operate by default on a particular {@link Scheduler}.
* * - * @param the type of the per-Observer state + * @param the type of the per-{@link Observer} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Observer - * @param generator the Consumer called with the current state whenever a particular downstream Observer has - * requested a value. The callback then should call {@code onNext}, {@code onError} or - * {@code onComplete} to signal a value or a terminal event. Signalling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @return the new Observable instance + * @param initialState the {@link Supplier} to generate the initial state for each {@code Observer} + * @param generator the {@link BiConsumer} called in a loop after a downstream {@code Observer} has + * subscribed. The callback then should call {@code onNext}, {@code onError} or + * {@code onComplete} to signal a value or a terminal event. Signaling multiple {@code onNext} + * in a call will make the operator signal {@link IllegalStateException}. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code initialState} or {@code generator} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable generate(Supplier initialState, final BiConsumer> generator) { - ObjectHelper.requireNonNull(generator, "generator is null"); + public static <@NonNull T, @NonNull S> Observable generate(@NonNull Supplier initialState, @NonNull BiConsumer> generator) { + Objects.requireNonNull(generator, "generator is null"); return generate(initialState, ObservableInternalHelper.simpleBiGenerator(generator), Functions.emptyConsumer()); } /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2052,32 +2336,33 @@ public static Observable generate(Supplier initialState, final BiCo *

{@code generate} does not operate by default on a particular {@link Scheduler}.
* * - * @param the type of the per-Observer state + * @param the type of the per-{@link Observer} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Observer - * @param generator the Consumer called with the current state whenever a particular downstream Observer has - * requested a value. The callback then should call {@code onNext}, {@code onError} or - * {@code onComplete} to signal a value or a terminal event. Signalling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @param disposeState the Consumer that is called with the current state when the generator + * @param initialState the {@link Supplier} to generate the initial state for each {@code Observer} + * @param generator the {@link BiConsumer} called in a loop after a downstream {@code Observer} has + * subscribed. The callback then should call {@code onNext}, {@code onError} or + * {@code onComplete} to signal a value or a terminal event. Signaling multiple {@code onNext} + * in a call will make the operator signal {@link IllegalStateException}. + * @param disposeState the {@link Consumer} that is called with the current state when the generator * terminates the sequence or it gets disposed - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code initialState}, {@code generator} or {@code disposeState} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable generate( - final Supplier initialState, - final BiConsumer> generator, - Consumer disposeState) { - ObjectHelper.requireNonNull(generator, "generator is null"); + public static <@NonNull T, @NonNull S> Observable generate( + @NonNull Supplier initialState, + @NonNull BiConsumer> generator, + @NonNull Consumer disposeState) { + Objects.requireNonNull(generator, "generator is null"); return generate(initialState, ObservableInternalHelper.simpleBiGenerator(generator), disposeState); } /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2088,26 +2373,28 @@ public static Observable generate( *

{@code generate} does not operate by default on a particular {@link Scheduler}.
* * - * @param the type of the per-Observer state + * @param the type of the per-{@link Observer} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Observer - * @param generator the Function called with the current state whenever a particular downstream Observer has - * requested a value. The callback then should call {@code onNext}, {@code onError} or + * @param initialState the {@link Supplier} to generate the initial state for each {@code Observer} + * @param generator the {@link BiConsumer} called in a loop after a downstream {@code Observer} has + * subscribed. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event and should return a (new) state for - * the next invocation. Signalling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @return the new Observable instance + * the next invocation. Signaling multiple {@code onNext} + * in a call will make the operator signal {@link IllegalStateException}. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code initialState} or {@code generator} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable generate(Supplier initialState, BiFunction, S> generator) { + @NonNull + public static <@NonNull T, @NonNull S> Observable generate(@NonNull Supplier initialState, @NonNull BiFunction, S> generator) { return generate(initialState, generator, Functions.emptyConsumer()); } /** * Returns a cold, synchronous and stateful generator of values. *

- * + * *

* Note that the {@link Emitter#onNext}, {@link Emitter#onError} and * {@link Emitter#onComplete} methods provided to the function via the {@link Emitter} instance should be called synchronously, @@ -2118,34 +2405,35 @@ public static Observable generate(Supplier initialState, BiFunction *

{@code generate} does not operate by default on a particular {@link Scheduler}.
* * - * @param the type of the per-Observer state + * @param the type of the per-{@link Observer} state * @param the generated value type - * @param initialState the Supplier to generate the initial state for each Observer - * @param generator the Function called with the current state whenever a particular downstream Observer has - * requested a value. The callback then should call {@code onNext}, {@code onError} or + * @param initialState the {@link Supplier} to generate the initial state for each {@code Observer} + * @param generator the {@link BiConsumer} called in a loop after a downstream {@code Observer} has + * subscribed. The callback then should call {@code onNext}, {@code onError} or * {@code onComplete} to signal a value or a terminal event and should return a (new) state for - * the next invocation. Signalling multiple {@code onNext} - * in a call will make the operator signal {@code IllegalStateException}. - * @param disposeState the Consumer that is called with the current state when the generator + * the next invocation. Signaling multiple {@code onNext} + * in a call will make the operator signal {@link IllegalStateException}. + * @param disposeState the {@link Consumer} that is called with the current state when the generator * terminates the sequence or it gets disposed - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code initialState}, {@code generator} or {@code disposeState} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable generate(Supplier initialState, BiFunction, S> generator, - Consumer disposeState) { - ObjectHelper.requireNonNull(initialState, "initialState is null"); - ObjectHelper.requireNonNull(generator, "generator is null"); - ObjectHelper.requireNonNull(disposeState, "disposeState is null"); - return RxJavaPlugins.onAssembly(new ObservableGenerate(initialState, generator, disposeState)); + public static <@NonNull T, @NonNull S> Observable generate(@NonNull Supplier initialState, @NonNull BiFunction, S> generator, + @NonNull Consumer disposeState) { + Objects.requireNonNull(initialState, "initialState is null"); + Objects.requireNonNull(generator, "generator is null"); + Objects.requireNonNull(disposeState, "disposeState is null"); + return RxJavaPlugins.onAssembly(new ObservableGenerate<>(initialState, generator, disposeState)); } /** - * Returns an Observable that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers + * Returns an {@code Observable} that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers * after each {@code period} of time thereafter. *

- * + * *

*
Scheduler:
*
{@code interval} operates by default on the {@code computation} {@link Scheduler}.
@@ -2157,25 +2445,26 @@ public static Observable generate(Supplier initialState, BiFunction * the period of time between emissions of the subsequent numbers * @param unit * the time unit for both {@code initialDelay} and {@code period} - * @return an Observable that emits a 0L after the {@code initialDelay} and ever increasing numbers after - * each {@code period} of time thereafter + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Interval + * @throws NullPointerException if {@code unit} is {@code null} * @since 1.0.12 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Observable interval(long initialDelay, long period, TimeUnit unit) { + @NonNull + public static Observable interval(long initialDelay, long period, @NonNull TimeUnit unit) { return interval(initialDelay, period, unit, Schedulers.computation()); } /** - * Returns an Observable that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers + * Returns an {@code Observable} that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers * after each {@code period} of time thereafter, on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param initialDelay @@ -2185,26 +2474,26 @@ public static Observable interval(long initialDelay, long period, TimeUnit * @param unit * the time unit for both {@code initialDelay} and {@code period} * @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 Scheduler + * the {@code Scheduler} on which the waiting happens and items are emitted + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Interval * @since 1.0.12 + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Observable interval(long initialDelay, long period, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public static Observable interval(long initialDelay, long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new ObservableInterval(Math.max(0L, initialDelay), Math.max(0L, period), unit, scheduler)); } /** - * Returns an Observable that emits a sequential number every specified interval of time. + * Returns an {@code Observable} that emits a sequential number every specified interval of time. *

- * + * *

*
Scheduler:
*
{@code interval} operates by default on the {@code computation} {@link Scheduler}.
@@ -2214,23 +2503,25 @@ public static Observable interval(long initialDelay, long period, TimeUnit * the period size in time units (see below) * @param unit * time units to use for the interval size - * @return an Observable that emits a sequential number each time interval + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Interval */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Observable interval(long period, TimeUnit unit) { + @NonNull + public static Observable interval(long period, @NonNull TimeUnit unit) { return interval(period, period, unit, Schedulers.computation()); } /** - * Returns an Observable that emits a sequential number every specified interval of time, on a - * specified Scheduler. + * Returns an {@code Observable} that emits a sequential number every specified interval of time, on a + * specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param period @@ -2238,13 +2529,15 @@ public static Observable interval(long period, TimeUnit unit) { * @param unit * time units to use for the interval size * @param scheduler - * the Scheduler to use for scheduling the items - * @return an Observable that emits a sequential number each time interval + * the {@code Scheduler} to use for scheduling the items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Interval */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Observable interval(long period, TimeUnit unit, Scheduler scheduler) { + @NonNull + public static Observable interval(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return interval(period, period, unit, scheduler); } @@ -2253,21 +2546,27 @@ public static Observable interval(long period, TimeUnit unit, Scheduler sc *

* The sequence completes immediately after the last value (start + count - 1) has been reached. *

- * + * *

*
Scheduler:
*
{@code intervalRange} by default operates on the {@link Schedulers#computation() computation} {@link Scheduler}.
*
* @param start that start value of the range - * @param count the number of values to emit in total, if zero, the operator emits an onComplete after the initial delay. - * @param initialDelay the initial delay before signalling the first value (the start) + * @param count the number of values to emit in total, if zero, the operator emits an {@code onComplete} after the initial delay. + * @param initialDelay the initial delay before signaling the first value (the start) * @param period the period between subsequent values - * @param unit the unit of measure of the initialDelay and period amounts - * @return the new Observable instance + * @param unit the unit of measure of the {@code initialDelay} and {@code period} amounts + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds + * {@link Long#MAX_VALUE} + * @see #range(int, int) */ @CheckReturnValue + @NonNull @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Observable intervalRange(long start, long count, long initialDelay, long period, TimeUnit unit) { + public static Observable intervalRange(long start, long count, long initialDelay, long period, @NonNull TimeUnit unit) { return intervalRange(start, count, initialDelay, period, unit, Schedulers.computation()); } @@ -2276,22 +2575,26 @@ public static Observable intervalRange(long start, long count, long initia *

* The sequence completes immediately after the last value (start + count - 1) has been reached. *

- * *

+ * *
*
Scheduler:
*
you provide the {@link Scheduler}.
*
* @param start that start value of the range - * @param count the number of values to emit in total, if zero, the operator emits an onComplete after the initial delay. - * @param initialDelay the initial delay before signalling the first value (the start) + * @param count the number of values to emit in total, if zero, the operator emits an {@code onComplete} after the initial delay. + * @param initialDelay the initial delay before signaling the first value (the start) * @param period the period between subsequent values - * @param unit the unit of measure of the initialDelay and period amounts + * @param unit the unit of measure of the {@code initialDelay} and {@code period} amounts * @param scheduler the target scheduler where the values and terminal signals will be emitted - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds + * {@link Long#MAX_VALUE} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Observable intervalRange(long start, long count, long initialDelay, long period, TimeUnit unit, Scheduler scheduler) { + public static Observable intervalRange(long start, long count, long initialDelay, long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); } @@ -2304,19 +2607,19 @@ public static Observable intervalRange(long start, long count, long initia if (start > 0 && end < 0) { throw new IllegalArgumentException("Overflow! start + count is bigger than Long.MAX_VALUE"); } - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new ObservableIntervalRange(start, end, Math.max(0L, initialDelay), Math.max(0L, period), unit, scheduler)); } /** - * Returns an Observable that signals the given (constant reference) item and then completes. + * Returns an {@code Observable} that signals the given (constant reference) item and then completes. *

* *

* Note that the item is taken and re-emitted as is and not computed by any means by {@code just}. Use {@link #fromCallable(Callable)} - * to generate a single item on demand (when {@code Observer}s subscribe to it). + * to generate a single item on demand (when {@link Observer}s subscribe to it). *

* See the multi-parameter overloads of {@code just} to emit more than one (constant reference) items one after the other. * Use {@link #fromArray(Object...)} to emit an arbitrary number of items that are known upfront. @@ -2331,7 +2634,8 @@ public static Observable intervalRange(long start, long count, long initia * the item to emit * @param * the type of that item - * @return an Observable that emits {@code value} as a single item and then completes + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Just * @see #just(Object, Object) * @see #fromCallable(Callable) @@ -2341,15 +2645,15 @@ public static Observable intervalRange(long start, long count, long initia @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item) { - ObjectHelper.requireNonNull(item, "item is null"); - return RxJavaPlugins.onAssembly(new ObservableJust(item)); + public static <@NonNull T> Observable just(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); + return RxJavaPlugins.onAssembly(new ObservableJust<>(item)); } /** - * Converts two items into an ObservableSource that emits those items. + * Converts two items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2361,24 +2665,24 @@ public static Observable just(T item) { * second item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1} or {@code item2} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); return fromArray(item1, item2); } /** - * Converts three items into an ObservableSource that emits those items. + * Converts three items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2392,25 +2696,25 @@ public static Observable just(T item1, T item2) { * third item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2} or {@code item3} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); return fromArray(item1, item2, item3); } /** - * Converts four items into an ObservableSource that emits those items. + * Converts four items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2426,26 +2730,26 @@ public static Observable just(T item1, T item2, T item3) { * fourth item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3} or {@code item4} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); return fromArray(item1, item2, item3, item4); } /** - * Converts five items into an ObservableSource that emits those items. + * Converts five items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2463,27 +2767,28 @@ public static Observable just(T item1, T item2, T item3, T item4) { * fifth item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4} or {@code item5} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4, T item5) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4, @NonNull T item5) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); return fromArray(item1, item2, item3, item4, item5); } /** - * Converts six items into an ObservableSource that emits those items. + * Converts six items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2503,28 +2808,29 @@ public static Observable just(T item1, T item2, T item3, T item4, T item5 * sixth item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5} or {@code item6} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4, T item5, T item6) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4, @NonNull T item5, @NonNull T item6) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); return fromArray(item1, item2, item3, item4, item5, item6); } /** - * Converts seven items into an ObservableSource that emits those items. + * Converts seven items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2546,29 +2852,31 @@ public static Observable just(T item1, T item2, T item3, T item4, T item5 * seventh item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6} + * or {@code item7} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4, @NonNull T item5, @NonNull T item6, @NonNull T item7) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7); } /** - * Converts eight items into an ObservableSource that emits those items. + * Converts eight items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2592,30 +2900,32 @@ public static Observable just(T item1, T item2, T item3, T item4, T item5 * eighth item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6} + * {@code item7} or {@code item8} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); - ObjectHelper.requireNonNull(item8, "item8 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4, @NonNull T item5, @NonNull T item6, @NonNull T item7, @NonNull T item8) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); + Objects.requireNonNull(item8, "item8 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7, item8); } /** - * Converts nine items into an ObservableSource that emits those items. + * Converts nine items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2641,31 +2951,33 @@ public static Observable just(T item1, T item2, T item3, T item4, T item5 * ninth item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6} + * {@code item7}, {@code item8} or {@code item9} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8, T item9) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); - ObjectHelper.requireNonNull(item8, "item8 is null"); - ObjectHelper.requireNonNull(item9, "item9 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4, @NonNull T item5, @NonNull T item6, @NonNull T item7, @NonNull T item8, @NonNull T item9) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); + Objects.requireNonNull(item8, "item8 is null"); + Objects.requireNonNull(item9, "item9 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7, item8, item9); } /** - * Converts ten items into an ObservableSource that emits those items. + * Converts ten items into an {@code Observable} that emits those items. *

- * + * *

*
Scheduler:
*
{@code just} does not operate by default on a particular {@link Scheduler}.
@@ -2693,47 +3005,50 @@ public static Observable just(T item1, T item2, T item3, T item4, T item5 * tenth item * @param * the type of these items - * @return an Observable that emits each item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item1}, {@code item2}, {@code item3}, + * {@code item4}, {@code item5}, {@code item6} + * {@code item7}, {@code item8}, {@code item9} + * or {@code item10} is {@code null} * @see ReactiveX operators documentation: Just */ - @SuppressWarnings("unchecked") @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Observable just(T item1, T item2, T item3, T item4, T item5, T item6, T item7, T item8, T item9, T item10) { - ObjectHelper.requireNonNull(item1, "item1 is null"); - ObjectHelper.requireNonNull(item2, "item2 is null"); - ObjectHelper.requireNonNull(item3, "item3 is null"); - ObjectHelper.requireNonNull(item4, "item4 is null"); - ObjectHelper.requireNonNull(item5, "item5 is null"); - ObjectHelper.requireNonNull(item6, "item6 is null"); - ObjectHelper.requireNonNull(item7, "item7 is null"); - ObjectHelper.requireNonNull(item8, "item8 is null"); - ObjectHelper.requireNonNull(item9, "item9 is null"); - ObjectHelper.requireNonNull(item10, "item10 is null"); + public static <@NonNull T> Observable just(@NonNull T item1, @NonNull T item2, @NonNull T item3, @NonNull T item4, @NonNull T item5, @NonNull T item6, @NonNull T item7, @NonNull T item8, @NonNull T item9, @NonNull T item10) { + Objects.requireNonNull(item1, "item1 is null"); + Objects.requireNonNull(item2, "item2 is null"); + Objects.requireNonNull(item3, "item3 is null"); + Objects.requireNonNull(item4, "item4 is null"); + Objects.requireNonNull(item5, "item5 is null"); + Objects.requireNonNull(item6, "item6 is null"); + Objects.requireNonNull(item7, "item7 is null"); + Objects.requireNonNull(item8, "item8 is null"); + Objects.requireNonNull(item9, "item9 is null"); + Objects.requireNonNull(item10, "item10 is null"); return fromArray(item1, item2, item3, item4, item5, item6, item7, item8, item9, item10); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, without any transformation, while limiting the - * number of concurrent subscriptions to these ObservableSources. + * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, without any transformation, while limiting the + * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

- * You can combine the items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the returned {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable, int, int)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -2743,44 +3058,45 @@ public static Observable just(T item1, T item2, T item3, T item4, T item5 * * @param the common element base type * @param sources - * the Iterable of ObservableSources + * the {@code Iterable} of {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner ObservableSource - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the number of items expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException - * if {@code maxConcurrent} is less than or equal to 0 + * if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Iterable, int, int) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable merge(Iterable> sources, int maxConcurrency, int bufferSize) { + @NonNull + public static <@NonNull T> Observable merge(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, int maxConcurrency, int bufferSize) { return fromIterable(sources).flatMap((Function)Functions.identity(), false, maxConcurrency, bufferSize); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, without any transformation, while limiting the - * number of concurrent subscriptions to these ObservableSources. + * Flattens an array of {@link ObservableSource}s into one {@code Observable}, without any transformation, while limiting the + * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

- * You can combine the items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeArrayDelayError(int, int, ObservableSource...)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -2790,43 +3106,45 @@ public static Observable merge(Iterable the common element base type * @param sources - * the array of ObservableSources + * the array of {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner ObservableSource - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the number of items expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException - * if {@code maxConcurrent} is less than or equal to 0 + * if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge * @see #mergeArrayDelayError(int, int, ObservableSource...) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeArray(int maxConcurrency, int bufferSize, ObservableSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Observable mergeArray(int maxConcurrency, int bufferSize, @NonNull ObservableSource... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), false, maxConcurrency, bufferSize); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, without any transformation. + * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, without any transformation. *

- * + * *

- * You can combine the items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the returned {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -2836,38 +3154,39 @@ public static Observable mergeArray(int maxConcurrency, int bufferSize, O * * @param the common element base type * @param sources - * the Iterable of ObservableSources - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the {@code Iterable} of {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Iterable) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable merge(Iterable> sources) { + @NonNull + public static <@NonNull T> Observable merge(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { return fromIterable(sources).flatMap((Function)Functions.identity()); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, without any transformation, while limiting the - * number of concurrent subscriptions to these ObservableSources. + * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, without any transformation, while limiting the + * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

- * You can combine the items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the returned {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable, int)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -2877,42 +3196,43 @@ public static Observable merge(Iterable the common element base type * @param sources - * the Iterable of ObservableSources + * the {@code Iterable} of {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException - * if {@code maxConcurrent} is less than or equal to 0 + * if {@code maxConcurrency} is less than or equal to 0 * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(Iterable, int) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable merge(Iterable> sources, int maxConcurrency) { + @NonNull + public static <@NonNull T> Observable merge(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, int maxConcurrency) { return fromIterable(sources).flatMap((Function)Functions.identity(), maxConcurrency); } /** - * Flattens an ObservableSource that emits ObservableSources into a single ObservableSource that emits the items emitted by - * those ObservableSources, without any transformation. + * Flattens an {@link ObservableSource} that emits {@code ObservableSource}s into a single {@code Observable} that emits the items emitted by + * those {@code ObservableSource}s, without any transformation. *

- * + * *

- * You can combine the items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the returned {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(ObservableSource)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -2922,40 +3242,41 @@ public static Observable merge(Iterable the common element base type * @param sources - * an ObservableSource that emits ObservableSources - * @return an Observable that emits items that are the result of flattening the ObservableSources emitted by the - * {@code source} ObservableSource + * an {@code ObservableSource} that emits {@code ObservableSource}s + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Merge + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeDelayError(ObservableSource) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Observable merge(ObservableSource> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @NonNull + public static <@NonNull T> Observable merge(@NonNull ObservableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return RxJavaPlugins.onAssembly(new ObservableFlatMap(sources, Functions.identity(), false, Integer.MAX_VALUE, bufferSize())); } /** - * Flattens an ObservableSource that emits ObservableSources into a single ObservableSource that emits the items emitted by - * those ObservableSources, without any transformation, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Flattens an {@link ObservableSource} that emits {@code ObservableSource}s into a single {@code Observable} that emits the items emitted by + * those {@code ObservableSource}s, without any transformation, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

- * You can combine the items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine the items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the returned {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(ObservableSource, int)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -2965,13 +3286,13 @@ public static Observable merge(ObservableSource the common element base type * @param sources - * an ObservableSource that emits ObservableSources + * an {@code ObservableSource} that emits {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits items that are the result of flattening the ObservableSources emitted by the - * {@code source} ObservableSource + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @throws IllegalArgumentException - * if {@code maxConcurrent} is less than or equal to 0 + * if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: Merge * @since 1.1.0 * @see #mergeDelayError(ObservableSource, int) @@ -2979,30 +3300,31 @@ public static Observable merge(ObservableSource Observable merge(ObservableSource> sources, int maxConcurrency) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @NonNull + public static <@NonNull T> Observable merge(@NonNull ObservableSource> sources, int maxConcurrency) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); return RxJavaPlugins.onAssembly(new ObservableFlatMap(sources, Functions.identity(), false, maxConcurrency, bufferSize())); } /** - * Flattens two ObservableSources into a single ObservableSource, without any transformation. + * Flattens two {@link ObservableSource}s into a single {@code Observable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(ObservableSource, ObservableSource)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -3012,40 +3334,42 @@ public static Observable merge(ObservableSource the common element base type * @param source1 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source2 - * an ObservableSource to be merged - * @return an Observable that emits all of the items emitted by the source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(ObservableSource, ObservableSource) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable merge(ObservableSource source1, ObservableSource source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + @NonNull + public static <@NonNull T> Observable merge(@NonNull ObservableSource source1, @NonNull ObservableSource source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return fromArray(source1, source2).flatMap((Function)Functions.identity(), false, 2); } /** - * Flattens three ObservableSources into a single ObservableSource, without any transformation. + * Flattens three {@link ObservableSource}s into a single {@code Observable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(ObservableSource, ObservableSource, ObservableSource)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -3055,43 +3379,47 @@ public static Observable merge(ObservableSource source1, Obs * * @param the common element base type * @param source1 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source2 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source3 - * an ObservableSource to be merged - * @return an Observable that emits all of the items emitted by the source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(ObservableSource, ObservableSource, ObservableSource) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable merge(ObservableSource source1, ObservableSource source2, ObservableSource source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + @NonNull + public static <@NonNull T> Observable merge( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return fromArray(source1, source2, source3).flatMap((Function)Functions.identity(), false, 3); } /** - * Flattens four ObservableSources into a single ObservableSource, without any transformation. + * Flattens four {@link ObservableSource}s into a single {@code Observable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(ObservableSource, ObservableSource, ObservableSource, ObservableSource)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -3101,48 +3429,50 @@ public static Observable merge(ObservableSource source1, Obs * * @param the common element base type * @param source1 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source2 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source3 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source4 - * an ObservableSource to be merged - * @return an Observable that emits all of the items emitted by the source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(ObservableSource, ObservableSource, ObservableSource, ObservableSource) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable merge( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + @NonNull + public static <@NonNull T> Observable merge( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return fromArray(source1, source2, source3, source4).flatMap((Function)Functions.identity(), false, 4); } /** - * Flattens an Array of ObservableSources into one ObservableSource, without any transformation. + * Flattens an array of {@link ObservableSource}s into one {@code Observable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code merge} method. *

*
Scheduler:
*
{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code ObservableSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the {@code ObservableSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Observable} terminates with that {@code Throwable} and all other source {@code ObservableSource}s are disposed. * If more than one {@code ObservableSource} signals an error, the resulting {@code Observable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Observable} has been disposed or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeArrayDelayError(ObservableSource...)} to merge sources and terminate only when all source {@code ObservableSource}s @@ -3152,31 +3482,34 @@ public static Observable merge( * * @param the common element base type * @param sources - * the array of ObservableSources - * @return an Observable that emits all of the items emitted by the ObservableSources in the Array + * the array of {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeArrayDelayError(ObservableSource...) */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeArray(ObservableSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Observable mergeArray(@NonNull ObservableSource... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), sources.length); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from each of the source ObservableSources without being interrupted by an error + * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from each of the returned {@code ObservableSource}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3184,31 +3517,32 @@ public static Observable mergeArray(ObservableSource... sour * * @param the common element base type * @param sources - * the Iterable of ObservableSources - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the {@code Iterable} of {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError(Iterable> sources) { + @NonNull + public static <@NonNull T> Observable mergeDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources) { return fromIterable(sources).flatMap((Function)Functions.identity(), true); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from each of the source ObservableSources without being interrupted by an error - * notification from one of them, while limiting the number of concurrent subscriptions to these ObservableSources. + * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from each of the returned {@code ObservableSource}s without being interrupted by an error + * notification from one of them, while limiting the number of concurrent subscriptions to these {@code ObservableSource}s. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3216,35 +3550,37 @@ public static Observable mergeDelayError(Iterable the common element base type * @param sources - * the Iterable of ObservableSources + * the {@code Iterable} of {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner ObservableSource - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the number of items expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError(Iterable> sources, int maxConcurrency, int bufferSize) { + @NonNull + public static <@NonNull T> Observable mergeDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, int maxConcurrency, int bufferSize) { return fromIterable(sources).flatMap((Function)Functions.identity(), true, maxConcurrency, bufferSize); } /** - * Flattens an array of ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from each of the source ObservableSources without being interrupted by an error - * notification from one of them, while limiting the number of concurrent subscriptions to these ObservableSources. + * Flattens an array of {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from each of the {@code ObservableSource}s without being interrupted by an error + * notification from one of them, while limiting the number of concurrent subscriptions to these {@code ObservableSource}s. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeArrayDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3252,35 +3588,38 @@ public static Observable mergeDelayError(Iterable the common element base type * @param sources - * the array of ObservableSources + * the array of {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param bufferSize - * the number of items to prefetch from each inner ObservableSource - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the number of items expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeArrayDelayError(int maxConcurrency, int bufferSize, ObservableSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Observable mergeArrayDelayError(int maxConcurrency, int bufferSize, @NonNull ObservableSource... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), true, maxConcurrency, bufferSize); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from each of the source ObservableSources without being interrupted by an error - * notification from one of them, while limiting the number of concurrent subscriptions to these ObservableSources. + * Flattens an {@link Iterable} of {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from each of the returned {@code ObservableSource}s without being interrupted by an error + * notification from one of them, while limiting the number of concurrent subscriptions to these {@code ObservableSource}s. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3288,33 +3627,35 @@ public static Observable mergeArrayDelayError(int maxConcurrency, int buf * * @param the common element base type * @param sources - * the Iterable of ObservableSources + * the {@code Iterable} of {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError(Iterable> sources, int maxConcurrency) { + @NonNull + public static <@NonNull T> Observable mergeDelayError(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, int maxConcurrency) { return fromIterable(sources).flatMap((Function)Functions.identity(), true, maxConcurrency); } /** - * Flattens an ObservableSource that emits ObservableSources into one ObservableSource, in a way that allows an Observer to - * receive all successfully emitted items from all of the source ObservableSources without being interrupted by + * Flattens an {@link ObservableSource} that emits {@code ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to + * receive all successfully emitted items from all of the emitted {@code ObservableSource}s without being interrupted by * an error notification from one of them. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3322,33 +3663,34 @@ public static Observable mergeDelayError(Iterable the common element base type * @param sources - * an ObservableSource that emits ObservableSources - * @return an Observable that emits all of the items emitted by the ObservableSources emitted by the - * {@code source} ObservableSource + * an {@code ObservableSource} that emits {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Observable mergeDelayError(ObservableSource> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @NonNull + public static <@NonNull T> Observable mergeDelayError(@NonNull ObservableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); return RxJavaPlugins.onAssembly(new ObservableFlatMap(sources, Functions.identity(), true, Integer.MAX_VALUE, bufferSize())); } /** - * Flattens an ObservableSource that emits ObservableSources into one ObservableSource, in a way that allows an Observer to - * receive all successfully emitted items from all of the source ObservableSources without being interrupted by + * Flattens an {@link ObservableSource} that emits {@code ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to + * receive all successfully emitted items from all of the emitted {@code ObservableSource}s without being interrupted by * an error notification from one of them, while limiting the - * number of concurrent subscriptions to these ObservableSources. + * number of concurrent subscriptions to these {@code ObservableSource}s. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3356,36 +3698,38 @@ public static Observable mergeDelayError(ObservableSource the common element base type * @param sources - * an ObservableSource that emits ObservableSources + * an {@code ObservableSource} that emits {@code ObservableSource}s * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits all of the items emitted by the ObservableSources emitted by the - * {@code source} ObservableSource + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: Merge * @since 2.0 */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError(ObservableSource> sources, int maxConcurrency) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @NonNull + public static <@NonNull T> Observable mergeDelayError(@NonNull ObservableSource> sources, int maxConcurrency) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); return RxJavaPlugins.onAssembly(new ObservableFlatMap(sources, Functions.identity(), true, maxConcurrency, bufferSize())); } /** - * Flattens two ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from each of the source ObservableSources without being interrupted by an error + * Flattens two {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from each of the {@code ObservableSource}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(ObservableSource, ObservableSource)} except that if any of the merged ObservableSources + * This behaves like {@link #merge(ObservableSource, ObservableSource)} except that if any of the merged {@code ObservableSource}s * notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from - * propagating that error notification until all of the merged ObservableSources have finished emitting items. + * propagating that error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3393,35 +3737,38 @@ public static Observable mergeDelayError(ObservableSource the common element base type * @param source1 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source2 - * an ObservableSource to be merged - * @return an Observable that emits all of the items that are emitted by the two source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError(ObservableSource source1, ObservableSource source2) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + @NonNull + public static <@NonNull T> Observable mergeDelayError( + @NonNull ObservableSource source1, @NonNull ObservableSource source2) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); return fromArray(source1, source2).flatMap((Function)Functions.identity(), true, 2); } /** - * Flattens three ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from all of the source ObservableSources without being interrupted by an error + * Flattens three {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from all of the {@code ObservableSource}s without being interrupted by an error * notification from one of them. *

* This behaves like {@link #merge(ObservableSource, ObservableSource, ObservableSource)} except that if any of the merged - * ObservableSources notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain - * from propagating that error notification until all of the merged ObservableSources have finished emitting + * {@code ObservableSource}s notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain + * from propagating that error notification until all of the merged {@code ObservableSource}s have finished emitting * items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3429,38 +3776,42 @@ public static Observable mergeDelayError(ObservableSource so * * @param the common element base type * @param source1 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source2 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source3 - * an ObservableSource to be merged - * @return an Observable that emits all of the items that are emitted by the source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError(ObservableSource source1, ObservableSource source2, ObservableSource source3) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + @NonNull + public static <@NonNull T> Observable mergeDelayError( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); return fromArray(source1, source2, source3).flatMap((Function)Functions.identity(), true, 3); } /** - * Flattens four ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from all of the source ObservableSources without being interrupted by an error + * Flattens four {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from all of the {@code ObservableSource}s without being interrupted by an error * notification from one of them. *

* This behaves like {@link #merge(ObservableSource, ObservableSource, ObservableSource, ObservableSource)} except that if any of - * the merged ObservableSources notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} - * will refrain from propagating that error notification until all of the merged ObservableSources have finished + * the merged {@code ObservableSource}s notify of an error via {@link Observer#onError onError}, {@code mergeDelayError} + * will refrain from propagating that error notification until all of the merged {@code ObservableSource}s have finished * emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3468,42 +3819,44 @@ public static Observable mergeDelayError(ObservableSource so * * @param the common element base type * @param source1 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source2 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source3 - * an ObservableSource to be merged + * an {@code ObservableSource} to be merged * @param source4 - * an ObservableSource to be merged - * @return an Observable that emits all of the items that are emitted by the source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeDelayError( - ObservableSource source1, ObservableSource source2, - ObservableSource source3, ObservableSource source4) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + @NonNull + public static <@NonNull T> Observable mergeDelayError( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); return fromArray(source1, source2, source3, source4).flatMap((Function)Functions.identity(), true, 4); } /** - * Flattens an Iterable of ObservableSources into one ObservableSource, in a way that allows an Observer to receive all - * successfully emitted items from each of the source ObservableSources without being interrupted by an error + * Flattens an array of {@link ObservableSource}s into one {@code Observable}, in a way that allows an {@link Observer} to receive all + * successfully emitted items from each of the {@code ObservableSource}s without being interrupted by an error * notification from one of them. *

- * This behaves like {@link #merge(ObservableSource)} except that if any of the merged ObservableSources notify of an + * This behaves like {@link #merge(ObservableSource)} except that if any of the merged {@code ObservableSource}s notify of an * error via {@link Observer#onError onError}, {@code mergeDelayError} will refrain from propagating that - * error notification until all of the merged ObservableSources have finished emitting items. + * error notification until all of the merged {@code ObservableSource}s have finished emitting items. *

- * + * *

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

*
Scheduler:
*
{@code mergeArrayDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3511,63 +3864,69 @@ public static Observable mergeDelayError( * * @param the common element base type * @param sources - * the Iterable of ObservableSources - * @return an Observable that emits items that are the result of flattening the items emitted by the - * ObservableSources in the Iterable + * the array of {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Merge */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable mergeArrayDelayError(ObservableSource... sources) { + @NonNull + @SafeVarargs + public static <@NonNull T> Observable mergeArrayDelayError(@NonNull ObservableSource... sources) { return fromArray(sources).flatMap((Function)Functions.identity(), true, sources.length); } /** - * Returns an Observable that never sends any items or notifications to an {@link Observer}. + * Returns an {@code Observable} that never sends any items or notifications to an {@link Observer}. *

- * + * *

- * This ObservableSource is useful primarily for testing purposes. + * The returned {@code Observable} is useful primarily for testing purposes. *

*
Scheduler:
*
{@code never} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items (not) emitted by the ObservableSource - * @return an Observable that never emits any items or sends any notifications to an {@link Observer} + * the type of items (not) emitted by the {@code Observable} + * @return the shared {@code Observable} instance * @see ReactiveX operators documentation: Never */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Observable never() { + @NonNull + public static <@NonNull T> Observable never() { return RxJavaPlugins.onAssembly((Observable) ObservableNever.INSTANCE); } /** - * Returns an Observable that emits a sequence of Integers within a specified range. + * Returns an {@code Observable} that emits a sequence of {@link Integer}s within a specified range. *

- * + * *

*
Scheduler:
*
{@code range} does not operate by default on a particular {@link Scheduler}.
*
* * @param start - * the value of the first Integer in the sequence + * the value of the first {@code Integer} in the sequence * @param count - * the number of sequential Integers to generate - * @return an Observable that emits a range of sequential Integers + * the number of sequential {@code Integer}s to generate + * @return the new {@code Observable} instance * @throws IllegalArgumentException - * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds - * {@code Integer.MAX_VALUE} + * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds + * {@link Integer#MAX_VALUE} * @see ReactiveX operators documentation: Range + * @see #rangeLong(long, long) + * @see #intervalRange(long, long, long, long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable range(final int start, final int count) { + @NonNull + public static Observable range(int start, int count) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); } @@ -3584,7 +3943,7 @@ public static Observable range(final int start, final int count) { } /** - * Returns an Observable that emits a sequence of Longs within a specified range. + * Returns an {@code Observable} that emits a sequence of {@link Long}s within a specified range. *

* *

@@ -3593,17 +3952,19 @@ public static Observable range(final int start, final int count) { *
* * @param start - * the value of the first Long in the sequence + * the value of the first {@code Long} in the sequence * @param count - * the number of sequential Longs to generate - * @return an Observable that emits a range of sequential Longs + * the number of sequential {@code Long}s to generate + * @return the new {@code Observable} instance * @throws IllegalArgumentException - * if {@code count} is less than zero, or if {@code start} + {@code count} − 1 exceeds - * {@code Long.MAX_VALUE} + * if {@code count} is negative, or if {@code start} + {@code count} − 1 exceeds + * {@link Long#MAX_VALUE} * @see ReactiveX operators documentation: Range + * @see #intervalRange(long, long, long, long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public static Observable rangeLong(long start, long count) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); @@ -3626,137 +3987,147 @@ public static Observable rangeLong(long start, long count) { } /** - * Returns a Single that emits a Boolean value that indicates whether two ObservableSource sequences are the - * same by comparing the items emitted by each ObservableSource pairwise. + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link ObservableSource} sequences are the + * same by comparing the items emitted by each {@code ObservableSource} pairwise. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first ObservableSource to compare + * the first {@code ObservableSource} to compare * @param source2 - * the second ObservableSource to compare + * the second {@code ObservableSource} to compare * @param - * the type of items emitted by each ObservableSource - * @return a Single that emits a Boolean value that indicates whether the two sequences are the same + * the type of items emitted by each {@code ObservableSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(ObservableSource source1, ObservableSource source2) { + @NonNull + public static <@NonNull T> Single sequenceEqual(@NonNull ObservableSource source1, @NonNull ObservableSource source2) { return sequenceEqual(source1, source2, ObjectHelper.equalsPredicate(), bufferSize()); } /** - * Returns a Single that emits a Boolean value that indicates whether two ObservableSource sequences are the - * same by comparing the items emitted by each ObservableSource pairwise based on the results of a specified + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link ObservableSource} sequences are the + * same by comparing the items emitted by each {@code ObservableSource} pairwise based on the results of a specified * equality function. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first ObservableSource to compare + * the first {@code ObservableSource} to compare * @param source2 - * the second ObservableSource to compare + * the second {@code ObservableSource} to compare * @param isEqual - * a function used to compare items emitted by each ObservableSource + * a function used to compare items emitted by each {@code ObservableSource} * @param - * the type of items emitted by each ObservableSource - * @return a Single that emits a Boolean value that indicates whether the two ObservableSource two sequences - * are the same according to the specified function + * the type of items emitted by each {@code ObservableSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code isEqual} is {@code null} * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(ObservableSource source1, ObservableSource source2, - BiPredicate isEqual) { + @NonNull + public static <@NonNull T> Single sequenceEqual( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull BiPredicate isEqual) { return sequenceEqual(source1, source2, isEqual, bufferSize()); } /** - * Returns a Single that emits a Boolean value that indicates whether two ObservableSource sequences are the - * same by comparing the items emitted by each ObservableSource pairwise based on the results of a specified + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link ObservableSource} sequences are the + * same by comparing the items emitted by each {@code ObservableSource} pairwise based on the results of a specified * equality function. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first ObservableSource to compare + * the first {@code ObservableSource} to compare * @param source2 - * the second ObservableSource to compare + * the second {@code ObservableSource} to compare * @param isEqual - * a function used to compare items emitted by each ObservableSource + * a function used to compare items emitted by each {@code ObservableSource} * @param bufferSize - * the number of items to prefetch from the first and second source ObservableSource + * the number of items expected from the first and second source {@code ObservableSource} to be buffered * @param - * the type of items emitted by each ObservableSource - * @return an Observable that emits a Boolean value that indicates whether the two ObservableSource two sequences - * are the same according to the specified function + * the type of items emitted by each {@code ObservableSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code isEqual} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(ObservableSource source1, ObservableSource source2, - BiPredicate isEqual, int bufferSize) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(isEqual, "isEqual is null"); + @NonNull + public static <@NonNull T> Single sequenceEqual( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull BiPredicate isEqual, int bufferSize) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(isEqual, "isEqual is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableSequenceEqualSingle(source1, source2, isEqual, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableSequenceEqualSingle<>(source1, source2, isEqual, bufferSize)); } /** - * Returns a Single that emits a Boolean value that indicates whether two ObservableSource sequences are the - * same by comparing the items emitted by each ObservableSource pairwise. + * Returns a {@link Single} that emits a {@link Boolean} value that indicates whether two {@link ObservableSource} sequences are the + * same by comparing the items emitted by each {@code ObservableSource} pairwise. *

- * + * *

*
Scheduler:
*
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* * @param source1 - * the first ObservableSource to compare + * the first {@code ObservableSource} to compare * @param source2 - * the second ObservableSource to compare + * the second {@code ObservableSource} to compare * @param bufferSize - * the number of items to prefetch from the first and second source ObservableSource + * the number of items expected from the first and second source {@code ObservableSource} to be buffered * @param - * the type of items emitted by each ObservableSource - * @return a Single that emits a Boolean value that indicates whether the two sequences are the same + * the type of items emitted by each {@code ObservableSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: SequenceEqual */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single sequenceEqual(ObservableSource source1, ObservableSource source2, + @NonNull + public static <@NonNull T> Single sequenceEqual(@NonNull ObservableSource source1, @NonNull ObservableSource source2, int bufferSize) { return sequenceEqual(source1, source2, ObjectHelper.equalsPredicate(), bufferSize); } /** - * Converts an ObservableSource that emits ObservableSources into an ObservableSource that emits the items emitted by the - * most recently emitted of those ObservableSources. + * Converts an {@link ObservableSource} that emits {@code ObservableSource}s into an {@code Observable} that emits the items emitted by the + * most recently emitted of those {@code ObservableSource}s. *

- * + * *

- * {@code switchOnNext} subscribes to an ObservableSource that emits ObservableSources. Each time it observes one of - * these emitted ObservableSources, the ObservableSource returned by {@code switchOnNext} begins emitting the items - * emitted by that ObservableSource. When a new ObservableSource is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted ObservableSource and begins emitting items from the new one. + * {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of + * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code ObservableSource}. When a new inner {@code ObservableSource} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code ObservableSource} and begins emitting items from the new one. *

- * The resulting ObservableSource completes if both the outer ObservableSource and the last inner ObservableSource, if any, complete. - * If the outer ObservableSource signals an onError, the inner ObservableSource is disposed and the error delivered in-sequence. + * The resulting {@code Observable} completes if both the outer {@code ObservableSource} and the last inner {@code ObservableSource}, if any, complete. + * If the outer {@code ObservableSource} signals an {@code onError}, the inner {@code ObservableSource} is disposed and the error delivered in-sequence. *

*
Scheduler:
*
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
@@ -3764,35 +4135,37 @@ public static Single sequenceEqual(ObservableSource so * * @param the item type * @param sources - * the source ObservableSource that emits ObservableSources + * the {@code ObservableSource} that emits {@code ObservableSource}s * @param bufferSize - * the number of items to prefetch from the inner ObservableSources - * @return an Observable that emits the items emitted by the ObservableSource most recently emitted by the source - * ObservableSource + * the expected number of items to cache from the inner {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Switch */ @SuppressWarnings({ "rawtypes", "unchecked" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable switchOnNext(ObservableSource> sources, int bufferSize) { - ObjectHelper.requireNonNull(sources, "sources is null"); + @NonNull + public static <@NonNull T> Observable switchOnNext(@NonNull ObservableSource> sources, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return RxJavaPlugins.onAssembly(new ObservableSwitchMap(sources, Functions.identity(), bufferSize, false)); } /** - * Converts an ObservableSource that emits ObservableSources into an ObservableSource that emits the items emitted by the - * most recently emitted of those ObservableSources. + * Converts an {@link ObservableSource} that emits {@code ObservableSource}s into an {@code Observable} that emits the items emitted by the + * most recently emitted of those {@code ObservableSource}s. *

- * + * *

- * {@code switchOnNext} subscribes to an ObservableSource that emits ObservableSources. Each time it observes one of - * these emitted ObservableSources, the ObservableSource returned by {@code switchOnNext} begins emitting the items - * emitted by that ObservableSource. When a new ObservableSource is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted ObservableSource and begins emitting items from the new one. + * {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of + * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code ObservableSource}. When a new inner {@code ObservableSource} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code ObservableSource} and begins emitting items from the new one. *

- * The resulting ObservableSource completes if both the outer ObservableSource and the last inner ObservableSource, if any, complete. - * If the outer ObservableSource signals an onError, the inner ObservableSource is disposed and the error delivered in-sequence. + * The resulting {@code Observable} completes if both the outer {@code ObservableSource} and the last inner {@code ObservableSource}, if any, complete. + * If the outer {@code ObservableSource} signals an {@code onError}, the inner {@code ObservableSource} is disposed and the error delivered in-sequence. *

*
Scheduler:
*
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
@@ -3800,31 +4173,32 @@ public static Observable switchOnNext(ObservableSource the item type * @param sources - * the source ObservableSource that emits ObservableSources - * @return an Observable that emits the items emitted by the ObservableSource most recently emitted by the source - * ObservableSource + * the {@code ObservableSource} that emits {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Switch */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable switchOnNext(ObservableSource> sources) { + @NonNull + public static <@NonNull T> Observable switchOnNext(@NonNull ObservableSource> sources) { return switchOnNext(sources, bufferSize()); } /** - * Converts an ObservableSource that emits ObservableSources into an ObservableSource that emits the items emitted by the - * most recently emitted of those ObservableSources and delays any exception until all ObservableSources terminate. + * Converts an {@link ObservableSource} that emits {@code ObservableSource}s into an {@code Observable} that emits the items emitted by the + * most recently emitted of those {@code ObservableSource}s and delays any exception until all {@code ObservableSource}s terminate. *

- * + * *

- * {@code switchOnNext} subscribes to an ObservableSource that emits ObservableSources. Each time it observes one of - * these emitted ObservableSources, the ObservableSource returned by {@code switchOnNext} begins emitting the items - * emitted by that ObservableSource. When a new ObservableSource is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted ObservableSource and begins emitting items from the new one. + * {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of + * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code ObservableSource}. When a new inner {@code ObservableSource} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code ObservableSource} and begins emitting items from the new one. *

- * The resulting ObservableSource completes if both the main ObservableSource and the last inner ObservableSource, if any, complete. - * If the main ObservableSource signals an onError, the termination of the last inner ObservableSource will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner ObservableSources signalled. + * The resulting {@code Observable} completes if both the main {@code ObservableSource} and the last inner {@code ObservableSource}, if any, complete. + * If the main {@code ObservableSource} signals an {@code onError}, the termination of the last inner {@code ObservableSource} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code ObservableSource}s signaled. *

*
Scheduler:
*
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3832,32 +4206,33 @@ public static Observable switchOnNext(ObservableSource the item type * @param sources - * the source ObservableSource that emits ObservableSources - * @return an Observable that emits the items emitted by the ObservableSource most recently emitted by the source - * ObservableSource + * the {@code ObservableSource} that emits {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see ReactiveX operators documentation: Switch * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable switchOnNextDelayError(ObservableSource> sources) { + @NonNull + public static <@NonNull T> Observable switchOnNextDelayError(@NonNull ObservableSource> sources) { return switchOnNextDelayError(sources, bufferSize()); } /** - * Converts an ObservableSource that emits ObservableSources into an ObservableSource that emits the items emitted by the - * most recently emitted of those ObservableSources and delays any exception until all ObservableSources terminate. + * Converts an {@link ObservableSource} that emits {@code ObservableSource}s into an {@code Observable} that emits the items emitted by the + * most recently emitted of those {@code ObservableSource}s and delays any exception until all {@code ObservableSource}s terminate. *

- * + * *

- * {@code switchOnNext} subscribes to an ObservableSource that emits ObservableSources. Each time it observes one of - * these emitted ObservableSources, the ObservableSource returned by {@code switchOnNext} begins emitting the items - * emitted by that ObservableSource. When a new ObservableSource is emitted, {@code switchOnNext} stops emitting items - * from the earlier-emitted ObservableSource and begins emitting items from the new one. + * {@code switchOnNext} subscribes to an {@code ObservableSource} that emits {@code ObservableSource}s. Each time it observes one of + * these emitted {@code ObservableSource}s, the {@code ObservableSource} returned by {@code switchOnNext} begins emitting the items + * emitted by that {@code ObservableSource}. When a new inner {@code ObservableSource} is emitted, {@code switchOnNext} stops emitting items + * from the earlier-emitted {@code ObservableSource} and begins emitting items from the new one. *

- * The resulting ObservableSource completes if both the main ObservableSource and the last inner ObservableSource, if any, complete. - * If the main ObservableSource signals an onError, the termination of the last inner ObservableSource will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner ObservableSources signalled. + * The resulting {@code Observable} completes if both the main {@code ObservableSource} and the last inner {@code ObservableSource}, if any, complete. + * If the main {@code ObservableSource} signals an {@code onError}, the termination of the last inner {@code ObservableSource} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code ObservableSource}s signaled. *

*
Scheduler:
*
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -3865,27 +4240,29 @@ public static Observable switchOnNextDelayError(ObservableSource the item type * @param sources - * the source ObservableSource that emits ObservableSources - * @param prefetch - * the number of items to prefetch from the inner ObservableSources - * @return an Observable that emits the items emitted by the ObservableSource most recently emitted by the source - * ObservableSource + * the {@code ObservableSource} that emits {@code ObservableSource}s + * @param bufferSize + * the expected number of items to cache from the inner {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Switch * @since 2.0 */ @SuppressWarnings({ "rawtypes", "unchecked" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable switchOnNextDelayError(ObservableSource> sources, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMap(sources, Functions.identity(), prefetch, true)); + @NonNull + public static <@NonNull T> Observable switchOnNextDelayError(@NonNull ObservableSource> sources, int bufferSize) { + Objects.requireNonNull(sources, "sources is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMap(sources, Functions.identity(), bufferSize, true)); } /** - * Returns an Observable that emits {@code 0L} after a specified delay, and then completes. + * Returns an {@code Observable} that emits {@code 0L} after a specified delay, and then completes. *

- * + * *

*
Scheduler:
*
{@code timer} operates by default on the {@code computation} {@link Scheduler}.
@@ -3895,23 +4272,25 @@ public static Observable switchOnNextDelayError(ObservableSourceReactiveX operators documentation: Timer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Observable timer(long delay, TimeUnit unit) { + @NonNull + public static Observable timer(long delay, @NonNull TimeUnit unit) { return timer(delay, unit, Schedulers.computation()); } /** - * Returns an Observable that emits {@code 0L} after a specified delay, on a specified Scheduler, and then + * Returns an {@code Observable} that emits {@code 0L} after a specified delay, on a specified {@link Scheduler}, and then * completes. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param delay @@ -3919,113 +4298,126 @@ public static Observable timer(long delay, TimeUnit unit) { * @param unit * time units to use for {@code delay} * @param scheduler - * the {@link Scheduler} to use for scheduling the item + * the {@code Scheduler} to use for scheduling the item * @throws NullPointerException - * if {@code unit} is null, or - * if {@code scheduler} is null - * @return an Observable that emits {@code 0L} after a specified delay, on a specified Scheduler, and then - * completes + * if {@code unit} or {@code scheduler} is {@code null} + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Timer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Observable timer(long delay, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public static Observable timer(long delay, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new ObservableTimer(Math.max(delay, 0L), unit, scheduler)); } /** - * Create an Observable by wrapping an ObservableSource which has to be implemented according - * to the Reactive-Streams-based Observable specification by handling - * disposal correctly; no safeguards are provided by the Observable itself. + * Create an {@code Observable} by wrapping an {@link ObservableSource} which has to be implemented according + * to the {@code Observable} specification derived from the Reactive Streams specification by handling + * disposal correctly; no safeguards are provided by the {@code Observable} itself. *
*
Scheduler:
*
{@code unsafeCreate} by default doesn't operate on any particular {@link Scheduler}.
*
* @param the value type emitted - * @param onSubscribe the ObservableSource instance to wrap - * @return the new Observable instance + * @param onSubscribe the {@code ObservableSource} instance to wrap + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} + * @throws IllegalArgumentException if the {@code onSubscribe} is already an {@code Observable}, use + * {@link #wrap(ObservableSource)} in this case + * @see #wrap(ObservableSource) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable unsafeCreate(ObservableSource onSubscribe) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); + @NonNull + public static <@NonNull T> Observable unsafeCreate(@NonNull ObservableSource onSubscribe) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); if (onSubscribe instanceof Observable) { throw new IllegalArgumentException("unsafeCreate(Observable) should be upgraded"); } - return RxJavaPlugins.onAssembly(new ObservableFromUnsafeSource(onSubscribe)); + return RxJavaPlugins.onAssembly(new ObservableFromUnsafeSource<>(onSubscribe)); } /** - * Constructs an ObservableSource that creates a dependent resource object which is disposed of when the downstream - * calls dispose(). + * Constructs an {@code Observable} that creates a dependent resource object, an {@link ObservableSource} with + * that resource and calls the provided {@code resourceDisposer} function if this inner source terminates or the + * downstream disposes the flow. *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the generated ObservableSource + * @param the element type of the generated {@code Observable} * @param the type of the resource associated with the output sequence * @param resourceSupplier - * the factory function to create a resource object that depends on the ObservableSource + * the factory function to create a resource object that depends on the {@code ObservableSource} * @param sourceSupplier - * the factory function to create an ObservableSource - * @param disposer + * the factory function to create an {@code ObservableSource} + * @param resourceCleanup * the function that will dispose of the resource - * @return the ObservableSource whose lifetime controls the lifetime of the dependent resource object + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} or {@code resourceCleanup} is {@code null} * @see ReactiveX operators documentation: Using */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable using(Supplier resourceSupplier, Function> sourceSupplier, Consumer disposer) { - return using(resourceSupplier, sourceSupplier, disposer, true); + @NonNull + public static <@NonNull T, @NonNull D> Observable using( + @NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup) { + return using(resourceSupplier, sourceSupplier, resourceCleanup, true); } /** - * Constructs an ObservableSource that creates a dependent resource object which is disposed of just before - * termination if you have set {@code disposeEagerly} to {@code true} and a dispose() call does not occur - * before termination. Otherwise resource disposal will occur on a dispose() call. Eager disposal is - * particularly appropriate for a synchronous ObservableSource that reuses resources. {@code disposeAction} will - * only be called once per subscription. + * Constructs an {@code Observable} that creates a dependent resource object, an {@link ObservableSource} with + * that resource and calls the provided {@code disposer} function if this inner source terminates or the + * downstream disposes the flow; doing it before these end-states have been reached if {@code eager == true}, after otherwise. *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the generated ObservableSource + * @param the element type of the generated {@code ObservableSource} * @param the type of the resource associated with the output sequence * @param resourceSupplier - * the factory function to create a resource object that depends on the ObservableSource + * the factory function to create a resource object that depends on the {@code ObservableSource} * @param sourceSupplier - * the factory function to create an ObservableSource - * @param disposer + * the factory function to create an {@code ObservableSource} + * @param resourceCleanup * the function that will dispose of the resource * @param eager - * If {@code true} then resource disposal will happen either on a {@code dispose()} call before the upstream is disposed + * If {@code true}, the resource disposal will happen either on a {@code dispose()} call before the upstream is disposed * or just before the emission of a terminal event ({@code onComplete} or {@code onError}). - * If {@code false} the resource disposal will happen either on a {@code dispose()} call after the upstream is disposed + * If {@code false}, the resource disposal will happen either on a {@code dispose()} call after the upstream is disposed * or just after the emission of a terminal event ({@code onComplete} or {@code onError}). - * @return the ObservableSource whose lifetime controls the lifetime of the dependent resource object + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} and {@code resourceCleanup} is {@code null} * @see ReactiveX operators documentation: Using * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable using(Supplier resourceSupplier, Function> sourceSupplier, Consumer disposer, boolean eager) { - ObjectHelper.requireNonNull(resourceSupplier, "resourceSupplier is null"); - ObjectHelper.requireNonNull(sourceSupplier, "sourceSupplier is null"); - ObjectHelper.requireNonNull(disposer, "disposer is null"); - return RxJavaPlugins.onAssembly(new ObservableUsing(resourceSupplier, sourceSupplier, disposer, eager)); + @NonNull + public static <@NonNull T, @NonNull D> Observable using( + @NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup, boolean eager) { + Objects.requireNonNull(resourceSupplier, "resourceSupplier is null"); + Objects.requireNonNull(sourceSupplier, "sourceSupplier is null"); + Objects.requireNonNull(resourceCleanup, "resourceCleanup is null"); + return RxJavaPlugins.onAssembly(new ObservableUsing(resourceSupplier, sourceSupplier, resourceCleanup, eager)); } /** - * Wraps an ObservableSource into an Observable if not already an Observable. + * Wraps an {@link ObservableSource} into an {@code Observable} if not already an {@code Observable}. * *
*
Scheduler:
@@ -4033,31 +4425,32 @@ public static Observable using(Supplier resourceSupplier, *
* * @param the value type - * @param source the source ObservableSource instance - * @return the new Observable instance or the same as the source - * @throws NullPointerException if source is null + * @param source the {@code ObservableSource} instance to wrap or cast to {@code Observable} + * @return the new {@code Observable} instance or the same as the source + * @throws NullPointerException if {@code source} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable wrap(ObservableSource source) { - ObjectHelper.requireNonNull(source, "source is null"); + @NonNull + public static <@NonNull T> Observable wrap(@NonNull ObservableSource source) { + Objects.requireNonNull(source, "source is null"); if (source instanceof Observable) { return RxJavaPlugins.onAssembly((Observable)source); } - return RxJavaPlugins.onAssembly(new ObservableFromUnsafeSource(source)); + return RxJavaPlugins.onAssembly(new ObservableFromUnsafeSource<>(source)); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an Iterable of other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an {@link Iterable} of other {@link ObservableSource}s. *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@code onNext} as many times as - * the number of {@code onNext} invocations of the source ObservableSource that emits the fewest items. + * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as + * the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -4073,10 +4466,10 @@ public static Observable wrap(ObservableSource source) { *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4085,32 +4478,34 @@ public static Observable wrap(ObservableSource source) { * @param the common value type * @param the zipped result type * @param sources - * an Iterable of source ObservableSources + * an {@code Iterable} of source {@code ObservableSource}s * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip(Iterable> sources, Function zipper) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new ObservableZip(null, sources, zipper, bufferSize(), false)); + @NonNull + public static <@NonNull T, @NonNull R> Observable zip(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, @NonNull Function zipper) { + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new ObservableZip<>(null, sources, zipper, bufferSize(), false)); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an Iterable of other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an {@link Iterable} of other {@link ObservableSource}s. *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@code onNext} as many times as - * the number of {@code onNext} invocations of the source ObservableSource that emits the fewest items. + * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as + * the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -4126,10 +4521,10 @@ public static Observable zip(Iterable * Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
@@ -4137,43 +4532,46 @@ public static Observable zip(Iterable the common source value type * @param the zipped result type - * @return an Observable that emits the zipped results + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip(Iterable> sources, - Function zipper, boolean delayError, + @NonNull + public static <@NonNull T, @NonNull R> Observable zip(@NonNull Iterable<@NonNull ? extends ObservableSource> sources, + @NonNull Function zipper, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableZip(null, sources, zipper, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new ObservableZip<>(null, sources, zipper, bufferSize, delayError)); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4196,39 +4594,41 @@ public static Observable zip(Iterable the value type of the second source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results - * in an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results + * in an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, - BiFunction zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull BiFunction zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4251,40 +4651,42 @@ public static Observable zip( * @param the value type of the second source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results - * in an item that will be emitted by the resulting ObservableSource - * @param delayError delay errors from any of the source ObservableSources till the other terminates - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results + * in an item that will be emitted by the resulting {@code Observable} + * @param delayError delay errors from any of the {@code ObservableSource}s till the other terminates + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, - BiFunction zipper, boolean delayError) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull BiFunction zipper, boolean delayError) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), delayError, bufferSize(), source1, source2); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * two items emitted, in sequence, by two other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * two items emitted, in sequence, by two other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4307,42 +4709,45 @@ public static Observable zip( * @param the value type of the second source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results - * in an item that will be emitted by the resulting ObservableSource - * @param delayError delay errors from any of the source ObservableSources till the other terminates - * @param bufferSize the number of elements to prefetch from each source ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results + * in an item that will be emitted by the resulting {@code Observable} + * @param delayError delay errors from any of the {@code ObservableSource}s till the other terminates + * @param bufferSize the number of elements expected from each source {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, - BiFunction zipper, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull BiFunction zipper, boolean delayError, int bufferSize) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), delayError, bufferSize, source1, source2); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * three items emitted, in sequence, by three other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * three items emitted, in sequence, by three other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4366,43 +4771,46 @@ public static Observable zip( * @param the value type of the third source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - Function3 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, + @NonNull Function3 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * four items emitted, in sequence, by four other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * four items emitted, in sequence, by four other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4427,47 +4835,50 @@ public static Observable zip( * @param the value type of the fourth source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param source4 - * a fourth source ObservableSource + * a fourth source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - ObservableSource source4, - Function4 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull Function4 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * five items emitted, in sequence, by five other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * five items emitted, in sequence, by five other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4493,49 +4904,52 @@ public static Observable zip( * @param the value type of the fifth source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param source4 - * a fourth source ObservableSource + * a fourth source {@code ObservableSource} * @param source5 - * a fifth source ObservableSource + * a fifth source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - ObservableSource source4, ObservableSource source5, - Function5 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, @NonNull ObservableSource source3, + @NonNull ObservableSource source4, @NonNull ObservableSource source5, + @NonNull Function5 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * six items emitted, in sequence, by six other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * six items emitted, in sequence, by six other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4562,52 +4976,55 @@ public static Observable zip( * @param the value type of the sixth source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param source4 - * a fourth source ObservableSource + * a fourth source {@code ObservableSource} * @param source5 - * a fifth source ObservableSource + * a fifth source {@code ObservableSource} * @param source6 - * a sixth source ObservableSource + * a sixth source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - ObservableSource source4, ObservableSource source5, ObservableSource source6, - Function6 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, @NonNull ObservableSource source3, + @NonNull ObservableSource source4, @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull Function6 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * seven items emitted, in sequence, by seven other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * seven items emitted, in sequence, by seven other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4635,56 +5052,60 @@ public static Observable zip( * @param the value type of the seventh source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param source4 - * a fourth source ObservableSource + * a fourth source {@code ObservableSource} * @param source5 - * a fifth source ObservableSource + * a fifth source {@code ObservableSource} * @param source6 - * a sixth source ObservableSource + * a sixth source {@code ObservableSource} * @param source7 - * a seventh source ObservableSource + * a seventh source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - ObservableSource source4, ObservableSource source5, ObservableSource source6, - ObservableSource source7, - Function7 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, @NonNull ObservableSource source3, + @NonNull ObservableSource source4, @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull ObservableSource source7, + @NonNull Function7 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6, source7); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * eight items emitted, in sequence, by eight other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * eight items emitted, in sequence, by eight other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4713,59 +5134,63 @@ public static Observable zip( * @param the value type of the eighth source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param source4 - * a fourth source ObservableSource + * a fourth source {@code ObservableSource} * @param source5 - * a fifth source ObservableSource + * a fifth source {@code ObservableSource} * @param source6 - * a sixth source ObservableSource + * a sixth source {@code ObservableSource} * @param source7 - * a seventh source ObservableSource + * a seventh source {@code ObservableSource} * @param source8 - * an eighth source ObservableSource + * an eighth source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - ObservableSource source4, ObservableSource source5, ObservableSource source6, - ObservableSource source7, ObservableSource source8, - Function8 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, @NonNull ObservableSource source3, + @NonNull ObservableSource source4, @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull ObservableSource source7, @NonNull ObservableSource source8, + @NonNull Function8 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6, source7, source8); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * nine items emitted, in sequence, by nine other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * nine items emitted, in sequence, by nine other {@link ObservableSource}s. *

- * + * *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@link Observer#onNext onNext} - * as many times as the number of {@code onNext} invocations of the source ObservableSource that emits the fewest + * The resulting {@code Observable} returned from {@code zip} will invoke {@link Observer#onNext onNext} + * as many times as the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest * items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if @@ -4795,60 +5220,64 @@ public static Observable zip( * @param the value type of the ninth source * @param the zipped result type * @param source1 - * the first source ObservableSource + * the first source {@code ObservableSource} * @param source2 - * a second source ObservableSource + * a second source {@code ObservableSource} * @param source3 - * a third source ObservableSource + * a third source {@code ObservableSource} * @param source4 - * a fourth source ObservableSource + * a fourth source {@code ObservableSource} * @param source5 - * a fifth source ObservableSource + * a fifth source {@code ObservableSource} * @param source6 - * a sixth source ObservableSource + * a sixth source {@code ObservableSource} * @param source7 - * a seventh source ObservableSource + * a seventh source {@code ObservableSource} * @param source8 - * an eighth source ObservableSource + * an eighth source {@code ObservableSource} * @param source9 - * a ninth source ObservableSource + * a ninth source {@code ObservableSource} * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource - * @return an Observable that emits the zipped results + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4}, {@code source5}, {@code source6}, + * {@code source7}, {@code source8}, {@code source9} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zip( - ObservableSource source1, ObservableSource source2, ObservableSource source3, - ObservableSource source4, ObservableSource source5, ObservableSource source6, - ObservableSource source7, ObservableSource source8, ObservableSource source9, - Function9 zipper) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); - ObjectHelper.requireNonNull(source9, "source9 is null"); + @NonNull + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> Observable zip( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, @NonNull ObservableSource source3, + @NonNull ObservableSource source4, @NonNull ObservableSource source5, @NonNull ObservableSource source6, + @NonNull ObservableSource source7, @NonNull ObservableSource source8, @NonNull ObservableSource source9, + @NonNull Function9 zipper) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), false, bufferSize(), source1, source2, source3, source4, source5, source6, source7, source8, source9); } /** - * Returns an Observable that emits the results of a specified combiner function applied to combinations of - * items emitted, in sequence, by an array of other ObservableSources. + * Returns an {@code Observable} that emits the results of a specified combiner function applied to combinations of + * items emitted, in sequence, by an array of other {@link ObservableSource}s. *

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

- * The resulting {@code ObservableSource} returned from {@code zip} will invoke {@code onNext} as many times as - * the number of {@code onNext} invocations of the source ObservableSource that emits the fewest items. + * The resulting {@code Observable} returned from {@code zip} will invoke {@code onNext} as many times as + * the number of {@code onNext} invocations of the {@code ObservableSource} that emits the fewest items. *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -4865,10 +5294,10 @@ public static Observable zip( *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

- * + * *

*
Scheduler:
*
{@code zipArray} does not operate by default on a particular {@link Scheduler}.
@@ -4877,27 +5306,34 @@ public static Observable zip( * @param the common element type * @param the result type * @param sources - * an array of source ObservableSources + * an array of source {@code ObservableSource}s * @param zipper - * a function that, when applied to an item emitted by each of the source ObservableSources, results in - * an item that will be emitted by the resulting ObservableSource + * a function that, when applied to an item emitted by each of the {@code ObservableSource}s, results in + * an item that will be emitted by the resulting {@code Observable} * @param delayError - * delay errors signalled by any of the source ObservableSource until all ObservableSources terminate + * delay errors signaled by any of the {@code ObservableSource} until all {@code ObservableSource}s terminate * @param bufferSize - * the number of elements to prefetch from each source ObservableSource - * @return an Observable that emits the zipped results + * the number of elements expected from each source {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sources} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Observable zipArray(Function zipper, - boolean delayError, int bufferSize, ObservableSource... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T, @NonNull R> Observable zipArray( + @NonNull Function zipper, + boolean delayError, int bufferSize, + @NonNull ObservableSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return empty(); } - ObjectHelper.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(zipper, "zipper is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableZip(sources, null, zipper, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new ObservableZip<>(sources, null, zipper, bufferSize, delayError)); } // *************************************************************************************************** @@ -4905,61 +5341,71 @@ public static Observable zipArray(Function - * + * *
*
Scheduler:
*
{@code all} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates an item and returns a Boolean - * @return a Single that emits {@code true} if all items emitted by the source ObservableSource satisfy the - * predicate; otherwise, {@code false} + * a function that evaluates an item and returns a {@code Boolean} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: All */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single all(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableAllSingle(this, predicate)); + @NonNull + public final Single all(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new ObservableAllSingle<>(this, predicate)); } /** - * Mirrors the ObservableSource (current or provided) that first either emits an item or sends a termination + * Mirrors the current {@code Observable} or the other {@link ObservableSource} provided of which the first either emits an item or sends a termination * notification. *

- * + * + *

+ * When the current {@code Observable} signals an item or terminates first, the subscription to the other + * {@code ObservableSource} is disposed. If the other {@code ObservableSource} signals an item or terminates first, + * the subscription to the current {@code Observable} is disposed. *

*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
+ * If the losing {@code ObservableSource} signals an error, the error is routed to the global + * error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
*
* * @param other - * an ObservableSource competing to react first. A subscription to this provided source will occur after + * an {@code ObservableSource} competing to react first. A subscription to this provided source will occur after * subscribing to the current source. - * @return an Observable that emits the same sequence as whichever of the source ObservableSources first - * emitted an item or sent a termination notification + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Amb */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable ambWith(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + @NonNull + public final Observable ambWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); return ambArray(this, other); } /** - * Returns a Single that emits {@code true} if any item emitted by the source ObservableSource satisfies a + * Returns a {@link Single} that emits {@code true} if any item emitted by the current {@code Observable} satisfies a * specified condition, otherwise {@code false}. Note: this always emits {@code false} if the - * source ObservableSource is empty. + * current {@code Observable} is empty. *

- * + * *

- * In Rx.Net this is the {@code any} Observer but we renamed it in RxJava to better match Java naming + * In Rx.Net this is the {@code any} {@link Observer} but we renamed it in RxJava to better match Java naming * idioms. *

*
Scheduler:
@@ -4967,37 +5413,43 @@ public final Observable ambWith(ObservableSource other) { *
* * @param predicate - * the condition to test items emitted by the source ObservableSource - * @return a Single that emits a Boolean that indicates whether any item emitted by the source - * ObservableSource satisfies the {@code predicate} + * the condition to test items emitted by the current {@code Observable} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single any(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableAnySingle(this, predicate)); + @NonNull + public final Single any(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new ObservableAnySingle<>(this, predicate)); } /** - * Returns the first item emitted by this {@code Observable}, or throws - * {@code NoSuchElementException} if it emits no items. + * Returns the first item emitted by the current {@code Observable}, or throws + * {@link NoSuchElementException} if it emits no items. *

- * + * *

*
Scheduler:
*
{@code blockingFirst} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the source signals an error, the operator wraps a checked {@link Exception} + * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and + * {@link Error}s are rethrown as they are.
*
* - * @return the first item emitted by this {@code Observable} + * @return the first item emitted by the current {@code Observable} * @throws NoSuchElementException - * if this {@code Observable} emits no items + * if the current {@code Observable} emits no items * @see ReactiveX documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final T blockingFirst() { - BlockingFirstObserver observer = new BlockingFirstObserver(); + BlockingFirstObserver observer = new BlockingFirstObserver<>(); subscribe(observer); T v = observer.blockingGet(); if (v != null) { @@ -5007,36 +5459,43 @@ public final T blockingFirst() { } /** - * Returns the first item emitted by this {@code Observable}, or a default value if it emits no + * Returns the first item emitted by the current {@code Observable}, or a default value if it emits no * items. *

* *

*
Scheduler:
*
{@code blockingFirst} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the source signals an error, the operator wraps a checked {@link Exception} + * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and + * {@link Error}s are rethrown as they are.
*
* * @param defaultItem - * a default value to return if this {@code Observable} emits no items - * @return the first item emitted by this {@code Observable}, or the default value if it emits no + * a default value to return if the current {@code Observable} emits no items + * @return the first item emitted by the current {@code Observable}, or the default value if it emits no * items + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingFirst(T defaultItem) { - BlockingFirstObserver observer = new BlockingFirstObserver(); + @NonNull + public final T blockingFirst(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + BlockingFirstObserver observer = new BlockingFirstObserver<>(); subscribe(observer); T v = observer.blockingGet(); return v != null ? v : defaultItem; } /** - * Consumes the upstream {@code Observable} in a blocking fashion and invokes the given - * {@code Consumer} with each upstream item on the current thread until the + * Consumes the current {@code Observable} in a blocking fashion and invokes the given + * {@link Consumer} with each upstream item on the current thread until the * upstream terminates. *

- * + * *

* Note: the method will only return if the upstream terminates or the current * thread is interrupted. @@ -5054,15 +5513,57 @@ public final T blockingFirst(T defaultItem) { *

* * @param onNext - * the {@link Consumer} to invoke for each item emitted by the {@code Observable} + * the {@code Consumer} to invoke for each item emitted by the {@code Observable} + * @throws NullPointerException if {@code onNext} is {@code null} * @throws RuntimeException * if an error occurs * @see ReactiveX documentation: Subscribe * @see #subscribe(Consumer) + * @see #blockingForEach(Consumer, int) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingForEach(@NonNull Consumer onNext) { + blockingForEach(onNext, bufferSize()); + } + + /** + * Consumes the current {@code Observable} in a blocking fashion and invokes the given + * {@link Consumer} with each upstream item on the current thread until the + * upstream terminates. + *

+ * + *

+ * Note: the method will only return if the upstream terminates or the current + * thread is interrupted. + *

+ * This method executes the {@code Consumer} on the current thread while + * {@link #subscribe(Consumer)} executes the consumer on the original caller thread of the + * sequence. + *

+ *
Scheduler:
+ *
{@code blockingForEach} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the source signals an error, the operator wraps a checked {@link Exception} + * into {@link RuntimeException} and throws that. Otherwise, {@code RuntimeException}s and + * {@link Error}s are rethrown as they are.
+ *
+ * + * @param onNext + * the {@code Consumer} to invoke for each item emitted by the {@code Observable} + * @param capacityHint + * the number of items expected to be buffered (allows reducing buffer reallocations) + * @throws NullPointerException if {@code onNext} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive + * @throws RuntimeException + * if an error occurs; {@code Error}s and {@code RuntimeException}s are rethrown + * as they are, checked {@code Exception}s are wrapped into {@code RuntimeException}s + * @see ReactiveX documentation: Subscribe + * @see #subscribe(Consumer) */ @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingForEach(Consumer onNext) { - Iterator it = blockingIterable().iterator(); + public final void blockingForEach(@NonNull Consumer onNext, int capacityHint) { + Objects.requireNonNull(onNext, "onNext is null"); + Iterator it = blockingIterable(capacityHint).iterator(); while (it.hasNext()) { try { onNext.accept(it.next()); @@ -5075,48 +5576,55 @@ public final void blockingForEach(Consumer onNext) { } /** - * Converts this {@code Observable} into an {@link Iterable}. + * Exposes the current {@code Observable} as an {@link Iterable} which, when iterated, + * subscribes to the current {@code Observable} and blocks + * until the current {@code Observable} emits items or terminates. *

- * + * *

*
Scheduler:
*
{@code blockingIterable} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@link Iterable} version of this {@code Observable} + * @return the new {@code Iterable} instance * @see ReactiveX documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingIterable() { return blockingIterable(bufferSize()); } /** - * Converts this {@code Observable} into an {@link Iterable}. + * Exposes the current {@code Observable} as an {@link Iterable} which, when iterated, + * subscribes to the current {@code Observable} and blocks + * until the current {@code Observable} emits items or terminates. *

- * + * *

*
Scheduler:
*
{@code blockingIterable} does not operate by default on a particular {@link Scheduler}.
*
* - * @param bufferSize the number of items to prefetch from the current Observable - * @return an {@link Iterable} version of this {@code Observable} + * @param capacityHint the expected number of items to be buffered + * @return the new {@code Iterable} instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Iterable blockingIterable(int bufferSize) { - ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return new BlockingObservableIterable(this, bufferSize); + @NonNull + public final Iterable blockingIterable(int capacityHint) { + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + return new BlockingObservableIterable<>(this, capacityHint); } /** - * Returns the last item emitted by this {@code Observable}, or throws - * {@code NoSuchElementException} if this {@code Observable} emits no items. + * Returns the last item emitted by the current {@code Observable}, or throws + * {@link NoSuchElementException} if the current {@code Observable} emits no items. *

- * + * *

*
Scheduler:
*
{@code blockingLast} does not operate by default on a particular {@link Scheduler}.
@@ -5126,15 +5634,16 @@ public final Iterable blockingIterable(int bufferSize) { * {@link Error}s are rethrown as they are.
*
* - * @return the last item emitted by this {@code Observable} + * @return the last item emitted by the current {@code Observable} * @throws NoSuchElementException - * if this {@code Observable} emits no items + * if the current {@code Observable} emits no items * @see ReactiveX documentation: Last */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final T blockingLast() { - BlockingLastObserver observer = new BlockingLastObserver(); + BlockingLastObserver observer = new BlockingLastObserver<>(); subscribe(observer); T v = observer.blockingGet(); if (v != null) { @@ -5144,10 +5653,10 @@ public final T blockingLast() { } /** - * Returns the last item emitted by this {@code Observable}, or a default value if it emits no + * Returns the last item emitted by the current {@code Observable}, or a default value if it emits no * items. *

- * + * *

*
Scheduler:
*
{@code blockingLast} does not operate by default on a particular {@link Scheduler}.
@@ -5158,27 +5667,30 @@ public final T blockingLast() { *
* * @param defaultItem - * a default value to return if this {@code Observable} emits no items + * a default value to return if the current {@code Observable} emits no items * @return the last item emitted by the {@code Observable}, or the default value if it emits no * items + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX documentation: Last */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingLast(T defaultItem) { - BlockingLastObserver observer = new BlockingLastObserver(); + @NonNull + public final T blockingLast(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + BlockingLastObserver observer = new BlockingLastObserver<>(); subscribe(observer); T v = observer.blockingGet(); return v != null ? v : defaultItem; } /** - * Returns an {@link Iterable} that returns the latest item emitted by this {@code Observable}, + * Returns an {@link Iterable} that returns the latest item emitted by the current {@code Observable}, * waiting if necessary for one to become available. *

* *

- * If this {@code Observable} produces items faster than {@code Iterator.next} takes them, + * If the current {@code Observable} produces items faster than {@code Iterator.next} takes them, * {@code onNext} events might be skipped, but {@code onError} or {@code onComplete} events are not. *

* Note also that an {@code onNext} directly followed by {@code onComplete} might hide the {@code onNext} @@ -5188,63 +5700,66 @@ public final T blockingLast(T defaultItem) { *

{@code blockingLatest} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an Iterable that always returns the latest item emitted by this {@code Observable} + * @return the new {@code Iterable} instance * @see ReactiveX documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingLatest() { - return new BlockingObservableLatest(this); + return new BlockingObservableLatest<>(this); } /** - * Returns an {@link Iterable} that always returns the item most recently emitted by this + * Returns an {@link Iterable} that always returns the item most recently emitted by the current * {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code blockingMostRecent} does not operate by default on a particular {@link Scheduler}.
*
* - * @param initialValue - * the initial value that the {@link Iterable} sequence will yield if this + * @param initialItem + * the initial value that the {@code Iterable} sequence will yield if the current * {@code Observable} has not yet emitted an item - * @return an {@link Iterable} that on each iteration returns the item that this {@code Observable} - * has most recently emitted + * @return the new {@code Iterable} instance + * @throws NullPointerException if {@code initialItem} is {@code null} * @see ReactiveX documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Iterable blockingMostRecent(T initialValue) { - return new BlockingObservableMostRecent(this, initialValue); + @NonNull + public final Iterable blockingMostRecent(@NonNull T initialItem) { + Objects.requireNonNull(initialItem, "initialItem is null"); + return new BlockingObservableMostRecent<>(this, initialItem); } /** - * Returns an {@link Iterable} that blocks until this {@code Observable} emits another item, then + * Returns an {@link Iterable} that blocks until the current {@code Observable} emits another item, then * returns that item. *

- * + * *

*
Scheduler:
*
{@code blockingNext} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@link Iterable} that blocks upon each iteration until this {@code Observable} emits - * a new item, whereupon the Iterable returns that item + * @return the new {@code Iterable} instance * @see ReactiveX documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Iterable blockingNext() { - return new BlockingObservableNext(this); + return new BlockingObservableNext<>(this); } /** - * If this {@code Observable} completes after emitting a single item, return that item, otherwise - * throw a {@code NoSuchElementException}. + * If the current {@code Observable} completes after emitting a single item, return that item, otherwise + * throw a {@link NoSuchElementException}. *

- * + * *

*
Scheduler:
*
{@code blockingSingle} does not operate by default on a particular {@link Scheduler}.
@@ -5254,11 +5769,12 @@ public final Iterable blockingNext() { * {@link Error}s are rethrown as they are.
*
* - * @return the single item emitted by this {@code Observable} + * @return the single item emitted by the current {@code Observable} * @see ReactiveX documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final T blockingSingle() { T v = singleElement().blockingGet(); if (v == null) { @@ -5268,11 +5784,11 @@ public final T blockingSingle() { } /** - * If this {@code Observable} completes after emitting a single item, return that item; if it emits - * more than one item, throw an {@code IllegalArgumentException}; if it emits no items, return a default + * If the current {@code Observable} completes after emitting a single item, return that item; if it emits + * more than one item, throw an {@link IllegalArgumentException}; if it emits no items, return a default * value. *

- * + * *

*
Scheduler:
*
{@code blockingSingle} does not operate by default on a particular {@link Scheduler}.
@@ -5283,25 +5799,27 @@ public final T blockingSingle() { *
* * @param defaultItem - * a default value to return if this {@code Observable} emits no items - * @return the single item emitted by this {@code Observable}, or the default value if it emits no + * a default value to return if the current {@code Observable} emits no items + * @return the single item emitted by the current {@code Observable}, or the default value if it emits no * items + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final T blockingSingle(T defaultItem) { + @NonNull + public final T blockingSingle(@NonNull T defaultItem) { return single(defaultItem).blockingGet(); } /** - * Returns a {@link Future} representing the only value emitted by this {@code Observable}. + * Returns a {@link Future} representing the only value emitted by the current {@code Observable}. *

- * + * *

- * If the {@link Observable} emits more than one item, {@link java.util.concurrent.Future} will receive an - * {@link java.lang.IndexOutOfBoundsException}. If the {@link Observable} is empty, {@link java.util.concurrent.Future} - * will receive an {@link java.util.NoSuchElementException}. The {@code Observable} source has to terminate in order + * If the {@code Observable} emits more than one item, {@code Future} will receive an + * {@link IndexOutOfBoundsException}. If the {@code Observable} is empty, {@code Future} + * will receive an {@link NoSuchElementException}. The {@code Observable} source has to terminate in order * for the returned {@code Future} to terminate as well. *

* If the {@code Observable} may emit more than one item, use {@code Observable.toList().toFuture()}. @@ -5310,17 +5828,19 @@ public final T blockingSingle(T defaultItem) { *

{@code toFuture} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Future} that expects a single item to be emitted by this {@code Observable} + * @return the new {@code Future} instance * @see ReactiveX documentation: To + * @see #singleOrErrorStage() */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Future toFuture() { - return subscribeWith(new FutureObserver()); + return subscribeWith(new FutureObserver<>()); } /** - * Runs the source observable to a terminal event, ignoring any values and rethrowing any exception. + * Runs the current {@code Observable} to a terminal event, ignoring any values and rethrowing any exception. *

* *

@@ -5344,11 +5864,11 @@ public final void blockingSubscribe() { /** * Subscribes to the source and calls the given callbacks on the current thread. *

- * + * *

* If the {@code Observable} emits an error, it is wrapped into an - * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * {@link OnErrorNotImplementedException} + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. * Using the overloads {@link #blockingSubscribe(Consumer, Consumer)} * or {@link #blockingSubscribe(Consumer, Consumer, Action)} instead is recommended. *

@@ -5360,19 +5880,20 @@ public final void blockingSubscribe() { *

{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
*
* @param onNext the callback action for each source value + * @throws NullPointerException if {@code onNext} is {@code null} * @since 2.0 * @see #blockingSubscribe(Consumer, Consumer) * @see #blockingSubscribe(Consumer, Consumer, Action) */ @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Consumer onNext) { + public final void blockingSubscribe(@NonNull Consumer onNext) { ObservableBlockingSubscribe.subscribe(this, onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** * Subscribes to the source and calls the given callbacks on the current thread. *

- * + * *

* Note that calling this method will block the caller thread until the upstream terminates * normally or with an error. Therefore, calling this method from special threads such as the @@ -5383,11 +5904,12 @@ public final void blockingSubscribe(Consumer onNext) { *

* @param onNext the callback action for each source value * @param onError the callback action for an error event + * @throws NullPointerException if {@code onNext} or {@code onError} is {@code null} * @since 2.0 * @see #blockingSubscribe(Consumer, Consumer, Action) */ @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Consumer onNext, Consumer onError) { + public final void blockingSubscribe(@NonNull Consumer onNext, @NonNull Consumer onError) { ObservableBlockingSubscribe.subscribe(this, onNext, onError, Functions.EMPTY_ACTION); } @@ -5406,10 +5928,11 @@ public final void blockingSubscribe(Consumer onNext, Consumer onNext, Consumer onError, Action onComplete) { + public final void blockingSubscribe(@NonNull Consumer onNext, @NonNull Consumer onError, @NonNull Action onComplete) { ObservableBlockingSubscribe.subscribe(this, onNext, onError, onComplete); } @@ -5427,21 +5950,23 @@ public final void blockingSubscribe(Consumer onNext, Consumer * The a dispose() call is composed through. * @param observer the {@code Observer} instance to forward events and calls to in the current thread + * @throws NullPointerException if {@code observer} is {@code null} * @since 2.0 */ @SchedulerSupport(SchedulerSupport.NONE) - public final void blockingSubscribe(Observer observer) { + public final void blockingSubscribe(@NonNull Observer observer) { + Objects.requireNonNull(observer, "observer is null"); ObservableBlockingSubscribe.subscribe(this, observer); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each containing {@code count} items. When the source - * ObservableSource completes, the resulting ObservableSource emits the current buffer and propagates the notification - * from the source ObservableSource. Note that if the source ObservableSource issues an onError notification + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each containing {@code count} items. When the current + * {@code Observable} completes, the resulting {@code Observable} emits the current buffer and propagates the notification + * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -5449,24 +5974,25 @@ public final void blockingSubscribe(Observer observer) { * * @param count * the maximum number of items in each buffer before it should be emitted - * @return an Observable that emits connected, non-overlapping buffers, each containing at most - * {@code count} items from the source ObservableSource + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> buffer(int count) { + @NonNull + public final Observable<@NonNull List> buffer(int count) { return buffer(count, count); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits buffers every {@code skip} items, each containing {@code count} items. When the source - * ObservableSource completes, the resulting ObservableSource emits the current buffer and propagates the notification - * from the source ObservableSource. Note that if the source ObservableSource issues an onError notification + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits buffers every {@code skip} items, each containing {@code count} items. When the current + * {@code Observable} completes, the resulting {@code Observable} emits the current buffer and propagates the notification + * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -5475,27 +6001,28 @@ public final Observable> buffer(int count) { * @param count * the maximum size of each buffer before it should be emitted * @param skip - * how many items emitted by the source ObservableSource should be skipped before starting a new + * how many items emitted by the current {@code Observable} should be skipped before starting a new * buffer. Note that when {@code skip} and {@code count} are equal, this is the same operation as * {@link #buffer(int)}. - * @return an Observable that emits buffers for every {@code skip} item from the source ObservableSource and - * containing at most {@code count} items + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code count} or {@code skip} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> buffer(int count, int skip) { - return buffer(count, skip, ArrayListSupplier.asSupplier()); + @NonNull + public final Observable<@NonNull List> buffer(int count, int skip) { + return buffer(count, skip, ArrayListSupplier.asSupplier()); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits buffers every {@code skip} items, each containing {@code count} items. When the source - * ObservableSource completes, the resulting ObservableSource emits the current buffer and propagates the notification - * from the source ObservableSource. Note that if the source ObservableSource issues an onError notification + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits buffers every {@code skip} items, each containing {@code count} items. When the current + * {@code Observable} completes, the resulting {@code Observable} emits the current buffer and propagates the notification + * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -5505,33 +6032,35 @@ public final Observable> buffer(int count, int skip) { * @param count * the maximum size of each buffer before it should be emitted * @param skip - * how many items emitted by the source ObservableSource should be skipped before starting a new + * how many items emitted by the current {@code Observable} should be skipped before starting a new * buffer. Note that when {@code skip} and {@code count} are equal, this is the same operation as * {@link #buffer(int)}. * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return an Observable that emits buffers for every {@code skip} item from the source ObservableSource and - * containing at most {@code count} items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code bufferSupplier} is {@code null} + * @throws IllegalArgumentException if {@code count} or {@code skip} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > Observable buffer(int count, int skip, Supplier bufferSupplier) { + @NonNull + public final <@NonNull U extends Collection> Observable buffer(int count, int skip, @NonNull Supplier bufferSupplier) { ObjectHelper.verifyPositive(count, "count"); ObjectHelper.verifyPositive(skip, "skip"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableBuffer(this, count, skip, bufferSupplier)); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableBuffer<>(this, count, skip, bufferSupplier)); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each containing {@code count} items. When the source - * ObservableSource completes, the resulting ObservableSource emits the current buffer and propagates the notification - * from the source ObservableSource. Note that if the source ObservableSource issues an onError notification + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each containing {@code count} items. When the current + * {@code Observable} completes, the resulting {@code Observable} emits the current buffer and propagates the notification + * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
@@ -5543,25 +6072,27 @@ public final > Observable buffer(int count, i * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return an Observable that emits connected, non-overlapping buffers, each containing at most - * {@code count} items from the source ObservableSource + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code bufferSupplier} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > Observable buffer(int count, Supplier bufferSupplier) { + @NonNull + public final <@NonNull U extends Collection> Observable buffer(int count, @NonNull Supplier bufferSupplier) { return buffer(count, count, bufferSupplier); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource starts a new buffer periodically, as determined by the {@code timeskip} argument. It emits - * each buffer after a fixed timespan, specified by the {@code timespan} argument. When the source - * ObservableSource completes, the resulting ObservableSource emits the current buffer and propagates the notification - * from the source ObservableSource. Note that if the source ObservableSource issues an onError notification + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} starts a new buffer periodically, as determined by the {@code timeskip} argument. It emits + * each buffer after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Observable} completes, the resulting {@code Observable} emits the current buffer and propagates the notification + * from the current {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification * the event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -5573,26 +6104,27 @@ public final > Observable buffer(int count, S * the period of time after which a new buffer will be created * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments - * @return an Observable that emits new buffers of items emitted by the source ObservableSource periodically after - * a fixed timespan has elapsed + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> buffer(long timespan, long timeskip, TimeUnit unit) { - return buffer(timespan, timeskip, unit, Schedulers.computation(), ArrayListSupplier.asSupplier()); + @NonNull + public final Observable<@NonNull List> buffer(long timespan, long timeskip, @NonNull TimeUnit unit) { + return buffer(timespan, timeskip, unit, Schedulers.computation(), ArrayListSupplier.asSupplier()); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the * specified {@code scheduler}. It emits each buffer after a fixed timespan, specified by the - * {@code timespan} argument. When the source ObservableSource completes, the resulting ObservableSource emits the - * current buffer and propagates the notification from the source ObservableSource. Note that if the source - * ObservableSource issues an onError notification the event is passed on immediately without first emitting the + * {@code timespan} argument. When the current {@code Observable} completes, the resulting {@code Observable} emits the + * current buffer and propagates the notification from the current {@code Observable}. Note that if the current + * {@code Observable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -5605,27 +6137,28 @@ public final Observable> buffer(long timespan, long timeskip, TimeUnit u * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer - * @return an Observable that emits new buffers of items emitted by the source ObservableSource periodically after - * a fixed timespan has elapsed + * the {@code Scheduler} to use when determining the end and start of a buffer + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> buffer(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler) { - return buffer(timespan, timeskip, unit, scheduler, ArrayListSupplier.asSupplier()); + @NonNull + public final Observable<@NonNull List> buffer(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return buffer(timespan, timeskip, unit, scheduler, ArrayListSupplier.asSupplier()); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} starts a new buffer periodically, as determined by the {@code timeskip} argument, and on the * specified {@code scheduler}. It emits each buffer after a fixed timespan, specified by the - * {@code timespan} argument. When the source ObservableSource completes, the resulting ObservableSource emits the - * current buffer and propagates the notification from the source ObservableSource. Note that if the source - * ObservableSource issues an onError notification the event is passed on immediately without first emitting the + * {@code timespan} argument. When the current {@code Observable} completes, the resulting {@code Observable} emits the + * current buffer and propagates the notification from the current {@code Observable}. Note that if the current + * {@code Observable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -5639,32 +6172,33 @@ public final Observable> buffer(long timespan, long timeskip, TimeUnit u * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer + * the {@code Scheduler} to use when determining the end and start of a buffer * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return an Observable that emits new buffers of items emitted by the source ObservableSource periodically after - * a fixed timespan has elapsed + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code bufferSupplier} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final > Observable buffer(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler, Supplier bufferSupplier) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableBufferTimed(this, timespan, timeskip, unit, scheduler, bufferSupplier, Integer.MAX_VALUE, false)); + @NonNull + public final <@NonNull U extends Collection> Observable buffer(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Supplier bufferSupplier) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableBufferTimed<>(this, timespan, timeskip, unit, scheduler, bufferSupplier, Integer.MAX_VALUE, false)); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each of a fixed duration specified by the - * {@code timespan} argument. When the source ObservableSource completes, the resulting ObservableSource emits the - * current buffer and propagates the notification from the source ObservableSource. Note that if the source - * ObservableSource issues an onError notification the event is passed on immediately without first emitting the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each of a fixed duration specified by the + * {@code timespan} argument. When the current {@code Observable} completes, the resulting {@code Observable} emits the + * current buffer and propagates the notification from the current {@code Observable}. Note that if the current + * {@code Observable} issues an {@code onError} notification the event is passed on immediately without first emitting the * buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -5675,26 +6209,27 @@ public final > Observable buffer(long timespa * buffer * @param unit * the unit of time that applies to the {@code timespan} argument - * @return an Observable that emits connected, non-overlapping buffers of items emitted by the source - * ObservableSource within a fixed duration + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> buffer(long timespan, TimeUnit unit) { + @NonNull + public final Observable<@NonNull List> buffer(long timespan, @NonNull TimeUnit unit) { return buffer(timespan, unit, Schedulers.computation(), Integer.MAX_VALUE); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each of a fixed duration specified by the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source ObservableSource completes, the resulting ObservableSource emits the current buffer and - * propagates the notification from the source ObservableSource. Note that if the source ObservableSource issues an - * onError notification the event is passed on immediately without first emitting the buffer it is in the process of + * first). When the current {@code Observable} completes, the resulting {@code Observable} emits the current buffer and + * propagates the notification from the current {@code Observable}. Note that if the current {@code Observable} issues an + * {@code onError} notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} operates by default on the {@code computation} {@link Scheduler}.
@@ -5707,27 +6242,28 @@ public final Observable> buffer(long timespan, TimeUnit unit) { * the unit of time which applies to the {@code timespan} argument * @param count * the maximum size of each buffer before it is emitted - * @return an Observable that emits connected, non-overlapping buffers of items emitted by the source - * ObservableSource, after a fixed duration or when the buffer reaches maximum capacity (whichever occurs - * first) + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> buffer(long timespan, TimeUnit unit, int count) { + @NonNull + public final Observable<@NonNull List> buffer(long timespan, @NonNull TimeUnit unit, int count) { return buffer(timespan, unit, Schedulers.computation(), count); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each of a fixed duration specified by the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each of a fixed duration specified by the * {@code timespan} argument as measured on the specified {@code scheduler}, or a maximum size specified by - * the {@code count} argument (whichever is reached first). When the source ObservableSource completes, the resulting - * ObservableSource emits the current buffer and propagates the notification from the source ObservableSource. Note - * that if the source ObservableSource issues an onError notification the event is passed on immediately without + * the {@code count} argument (whichever is reached first). When the current {@code Observable} completes, the resulting + * {@code Observable} emits the current buffer and propagates the notification from the current {@code Observable}. Note + * that if the current {@code Observable} issues an {@code onError} notification the event is passed on immediately without * first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -5739,30 +6275,31 @@ public final Observable> buffer(long timespan, TimeUnit unit, int count) * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer + * the {@code Scheduler} to use when determining the end and start of a buffer * @param count * the maximum size of each buffer before it is emitted - * @return an Observable that emits connected, non-overlapping buffers of items emitted by the source - * ObservableSource after a fixed duration or when the buffer reaches maximum capacity (whichever occurs - * first) + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler, int count) { - return buffer(timespan, unit, scheduler, count, ArrayListSupplier.asSupplier(), false); + @NonNull + public final Observable<@NonNull List> buffer(long timespan, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, int count) { + return buffer(timespan, unit, scheduler, count, ArrayListSupplier.asSupplier(), false); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each of a fixed duration specified by the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each of a fixed duration specified by the * {@code timespan} argument as measured on the specified {@code scheduler}, or a maximum size specified by - * the {@code count} argument (whichever is reached first). When the source ObservableSource completes, the resulting - * ObservableSource emits the current buffer and propagates the notification from the source ObservableSource. Note - * that if the source ObservableSource issues an onError notification the event is passed on immediately without + * the {@code count} argument (whichever is reached first). When the current {@code Observable} completes, the resulting + * {@code Observable} emits the current buffer and propagates the notification from the current {@code Observable}. Note + * that if the current {@code Observable} issues an {@code onError} notification the event is passed on immediately without * first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -5775,42 +6312,43 @@ public final Observable> buffer(long timespan, TimeUnit unit, Scheduler * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer + * the {@code Scheduler} to use when determining the end and start of a buffer * @param count * the maximum size of each buffer before it is emitted * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @param restartTimerOnMaxSize if true the time window is restarted when the max capacity of the current buffer + * @param restartTimerOnMaxSize if {@code true}, the time window is restarted when the max capacity of the current buffer * is reached - * @return an Observable that emits connected, non-overlapping buffers of items emitted by the source - * ObservableSource after a fixed duration or when the buffer reaches maximum capacity (whichever occurs - * first) + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code bufferSupplier} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final > Observable buffer( - long timespan, TimeUnit unit, - Scheduler scheduler, int count, - Supplier bufferSupplier, + @NonNull + public final <@NonNull U extends Collection> Observable buffer( + long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, int count, + @NonNull Supplier bufferSupplier, boolean restartTimerOnMaxSize) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); ObjectHelper.verifyPositive(count, "count"); - return RxJavaPlugins.onAssembly(new ObservableBufferTimed(this, timespan, timespan, unit, scheduler, bufferSupplier, count, restartTimerOnMaxSize)); + return RxJavaPlugins.onAssembly(new ObservableBufferTimed<>(this, timespan, timespan, unit, scheduler, bufferSupplier, count, restartTimerOnMaxSize)); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping buffers, each of a fixed duration specified by the - * {@code timespan} argument and on the specified {@code scheduler}. When the source ObservableSource completes, - * the resulting ObservableSource emits the current buffer and propagates the notification from the source - * ObservableSource. Note that if the source ObservableSource issues an onError notification the event is passed on + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping buffers, each of a fixed duration specified by the + * {@code timespan} argument and on the specified {@code scheduler}. When the current {@code Observable} completes, + * the resulting {@code Observable} emits the current buffer and propagates the notification from the current + * {@code Observable}. Note that if the current {@code Observable} issues an {@code onError} notification the event is passed on * immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -5822,98 +6360,101 @@ public final > Observable buffer( * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a buffer - * @return an Observable that emits connected, non-overlapping buffers of items emitted by the source - * ObservableSource within a fixed duration + * the {@code Scheduler} to use when determining the end and start of a buffer + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> buffer(long timespan, TimeUnit unit, Scheduler scheduler) { - return buffer(timespan, unit, scheduler, Integer.MAX_VALUE, ArrayListSupplier.asSupplier(), false); + @NonNull + public final Observable<@NonNull List> buffer(long timespan, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return buffer(timespan, unit, scheduler, Integer.MAX_VALUE, ArrayListSupplier.asSupplier(), false); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits buffers that it creates when the specified {@code openingIndicator} ObservableSource emits an - * item, and closes when the ObservableSource returned from {@code closingIndicator} emits an item. If any of the - * source ObservableSource, {@code openingIndicator} or {@code closingIndicator} issues an onError notification the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits buffers that it creates when the specified {@code openingIndicator} {@link ObservableSource} emits an + * item, and closes when the {@code ObservableSource} returned from {@code closingIndicator} emits an item. If any of the + * current {@code Observable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the * event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the buffer-opening ObservableSource - * @param the element type of the individual buffer-closing ObservableSources + * @param the element type of the buffer-opening {@code ObservableSource} + * @param the element type of the individual buffer-closing {@code ObservableSource}s * @param openingIndicator - * the ObservableSource that, when it emits an item, causes a new buffer to be created + * the {@code ObservableSource} that, when it emits an item, causes a new buffer to be created * @param closingIndicator - * the {@link Function} that is used to produce an ObservableSource for every buffer created. When this - * ObservableSource emits an item, the associated buffer is emitted. - * @return an Observable that emits buffers, containing items from the source ObservableSource, that are created - * and closed when the specified ObservableSources emit items + * the {@link Function} that is used to produce an {@code ObservableSource} for every buffer created. When this indicator + * {@code ObservableSource} emits an item, the associated buffer is emitted. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code openingIndicator} or {@code closingIndicator} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> buffer( - ObservableSource openingIndicator, - Function> closingIndicator) { - return buffer(openingIndicator, closingIndicator, ArrayListSupplier.asSupplier()); + @NonNull + public final <@NonNull TOpening, @NonNull TClosing> Observable<@NonNull List> buffer( + @NonNull ObservableSource openingIndicator, + @NonNull Function> closingIndicator) { + return buffer(openingIndicator, closingIndicator, ArrayListSupplier.asSupplier()); } /** - * Returns an Observable that emits buffers of items it collects from the source ObservableSource. The resulting - * ObservableSource emits buffers that it creates when the specified {@code openingIndicator} ObservableSource emits an - * item, and closes when the ObservableSource returned from {@code closingIndicator} emits an item. If any of the - * source ObservableSource, {@code openingIndicator} or {@code closingIndicator} issues an onError notification the + * Returns an {@code Observable} that emits buffers of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits buffers that it creates when the specified {@code openingIndicator} {@link ObservableSource} emits an + * item, and closes when the {@code ObservableSource} returned from {@code closingIndicator} emits an item. If any of the + * current {@code Observable}, {@code openingIndicator} or {@code closingIndicator} issues an {@code onError} notification the * event is passed on immediately without first emitting the buffer it is in the process of assembling. *

- * + * *

*
Scheduler:
*
This version of {@code buffer} does not operate by default on a particular {@link Scheduler}.
*
* * @param the collection subclass type to buffer into - * @param the element type of the buffer-opening ObservableSource - * @param the element type of the individual buffer-closing ObservableSources + * @param the element type of the buffer-opening {@code ObservableSource} + * @param the element type of the individual buffer-closing {@code ObservableSource}s * @param openingIndicator - * the ObservableSource that, when it emits an item, causes a new buffer to be created + * the {@code ObservableSource} that, when it emits an item, causes a new buffer to be created * @param closingIndicator - * the {@link Function} that is used to produce an ObservableSource for every buffer created. When this - * ObservableSource emits an item, the associated buffer is emitted. + * the {@link Function} that is used to produce an {@code ObservableSource} for every buffer created. When this indicator + * {@code ObservableSource} emits an item, the associated buffer is emitted. * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return an Observable that emits buffers, containing items from the source ObservableSource, that are created - * and closed when the specified ObservableSources emit items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code openingIndicator}, {@code closingIndicator} or {@code bufferSupplier} is {@code null} * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > Observable buffer( - ObservableSource openingIndicator, - Function> closingIndicator, - Supplier bufferSupplier) { - ObjectHelper.requireNonNull(openingIndicator, "openingIndicator is null"); - ObjectHelper.requireNonNull(closingIndicator, "closingIndicator is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); + @NonNull + public final <@NonNull TOpening, @NonNull TClosing, @NonNull U extends Collection> Observable buffer( + @NonNull ObservableSource openingIndicator, + @NonNull Function> closingIndicator, + @NonNull Supplier bufferSupplier) { + Objects.requireNonNull(openingIndicator, "openingIndicator is null"); + Objects.requireNonNull(closingIndicator, "closingIndicator is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); return RxJavaPlugins.onAssembly(new ObservableBufferBoundary(this, openingIndicator, closingIndicator, bufferSupplier)); } /** - * Returns an Observable that emits non-overlapping buffered items from the source ObservableSource each time the - * specified boundary ObservableSource emits an item. + * Returns an {@code Observable} that emits non-overlapping buffered items from the current {@code Observable} each time the + * specified boundary {@link ObservableSource} emits an item. *

- * + * *

- * Completion of either the source or the boundary ObservableSource causes the returned ObservableSource to emit the - * latest buffer and complete. If either the source ObservableSource or the boundary ObservableSource issues an - * onError notification the event is passed on immediately without first emitting the buffer it is in the process of + * Completion of either the source or the boundary {@code ObservableSource} causes the returned {@code ObservableSource} to emit the + * latest buffer and complete. If either the current {@code Observable} or the boundary {@code ObservableSource} issues an + * {@code onError} notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

*
Scheduler:
@@ -5922,28 +6463,29 @@ public final > Observable * * @param * the boundary value type (ignored) - * @param boundary - * the boundary ObservableSource - * @return an Observable that emits buffered items from the source ObservableSource when the boundary ObservableSource - * emits an item + * @param boundaryIndicator + * the boundary {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} * @see #buffer(ObservableSource, int) * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> buffer(ObservableSource boundary) { - return buffer(boundary, ArrayListSupplier.asSupplier()); + @NonNull + public final <@NonNull B> Observable<@NonNull List> buffer(@NonNull ObservableSource boundaryIndicator) { + return buffer(boundaryIndicator, ArrayListSupplier.asSupplier()); } /** - * Returns an Observable that emits non-overlapping buffered items from the source ObservableSource each time the - * specified boundary ObservableSource emits an item. + * Returns an {@code Observable} that emits non-overlapping buffered items from the current {@code Observable} each time the + * specified boundary {@link ObservableSource} emits an item. *

- * + * *

- * Completion of either the source or the boundary ObservableSource causes the returned ObservableSource to emit the - * latest buffer and complete. If either the source ObservableSource or the boundary ObservableSource issues an - * onError notification the event is passed on immediately without first emitting the buffer it is in the process of + * Completion of either the source or the boundary {@code ObservableSource} causes the returned {@code ObservableSource} to emit the + * latest buffer and complete. If either the current {@code Observable} or the boundary {@code ObservableSource} issues an + * {@code onError} notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

*
Scheduler:
@@ -5952,31 +6494,33 @@ public final Observable> buffer(ObservableSource boundary) { * * @param * the boundary value type (ignored) - * @param boundary - * the boundary ObservableSource + * @param boundaryIndicator + * the boundary {@code ObservableSource} * @param initialCapacity * the initial capacity of each buffer chunk - * @return an Observable that emits buffered items from the source ObservableSource when the boundary ObservableSource - * emits an item + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Buffer + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} + * @throws IllegalArgumentException if {@code initialCapacity} is non-positive * @see #buffer(ObservableSource) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> buffer(ObservableSource boundary, final int initialCapacity) { + @NonNull + public final <@NonNull B> Observable<@NonNull List> buffer(@NonNull ObservableSource boundaryIndicator, int initialCapacity) { ObjectHelper.verifyPositive(initialCapacity, "initialCapacity"); - return buffer(boundary, Functions.createArrayList(initialCapacity)); + return buffer(boundaryIndicator, Functions.createArrayList(initialCapacity)); } /** - * Returns an Observable that emits non-overlapping buffered items from the source ObservableSource each time the - * specified boundary ObservableSource emits an item. + * Returns an {@code Observable} that emits non-overlapping buffered items from the current {@code Observable} each time the + * specified boundary {@link ObservableSource} emits an item. *

- * + * *

- * Completion of either the source or the boundary ObservableSource causes the returned ObservableSource to emit the - * latest buffer and complete. If either the source ObservableSource or the boundary ObservableSource issues an - * onError notification the event is passed on immediately without first emitting the buffer it is in the process of + * Completion of either the source or the boundary {@code ObservableSource} causes the returned {@code ObservableSource} to emit the + * latest buffer and complete. If either the current {@code Observable} or the boundary {@code ObservableSource} issues an + * {@code onError} notification the event is passed on immediately without first emitting the buffer it is in the process of * assembling. *

*
Scheduler:
@@ -5986,42 +6530,43 @@ public final Observable> buffer(ObservableSource boundary, final * @param the collection subclass type to buffer into * @param * the boundary value type (ignored) - * @param boundary - * the boundary ObservableSource + * @param boundaryIndicator + * the boundary {@code ObservableSource} * @param bufferSupplier * a factory function that returns an instance of the collection subclass to be used and returned * as the buffer - * @return an Observable that emits buffered items from the source ObservableSource when the boundary ObservableSource - * emits an item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code boundaryIndicator} or {@code bufferSupplier} is {@code null} * @see #buffer(ObservableSource, int) * @see ReactiveX operators documentation: Buffer */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > Observable buffer(ObservableSource boundary, Supplier bufferSupplier) { - ObjectHelper.requireNonNull(boundary, "boundary is null"); - ObjectHelper.requireNonNull(bufferSupplier, "bufferSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableBufferExactBoundary(this, boundary, bufferSupplier)); + @NonNull + public final <@NonNull B, @NonNull U extends Collection> Observable buffer(@NonNull ObservableSource boundaryIndicator, @NonNull Supplier bufferSupplier) { + Objects.requireNonNull(boundaryIndicator, "boundaryIndicator is null"); + Objects.requireNonNull(bufferSupplier, "bufferSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableBufferExactBoundary<>(this, boundaryIndicator, bufferSupplier)); } /** - * Returns an Observable that subscribes to this ObservableSource lazily, caches all of its events - * and replays them, in the same order as received, to all the downstream subscribers. + * Returns an {@code Observable} that subscribes to the current {@code Observable} lazily, caches all of its events + * and replays them, in the same order as received, to all the downstream observers. *

- * + * *

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

- * The operator subscribes only when the first downstream subscriber subscribes and maintains - * a single subscription towards this ObservableSource. In contrast, the operator family of {@link #replay()} + * The operator subscribes only when the first downstream observer subscribes and maintains + * a single subscription towards the current {@code Observable}. In contrast, the operator family of {@link #replay()} * that return a {@link ConnectableObservable} require an explicit call to {@link ConnectableObservable#connect()}. *

* Note: You sacrifice the ability to dispose the origin when you use the {@code cache} - * Observer so be careful not to use this Observer on ObservableSources that emit an infinite or very large number + * operator so be careful not to use this operator on {@code Observable}s that emit an infinite or very large number * of items that will use up memory. - * A possible workaround is to apply `takeUntil` with a predicate or - * another source before (and perhaps after) the application of cache(). + * A possible workaround is to apply {@code takeUntil} with a predicate or + * another source before (and perhaps after) the application of {@code cache()}. *


      * AtomicBoolean shouldStop = new AtomicBoolean();
      *
@@ -6048,34 +6593,36 @@ public final > Observable buffer(Observabl
      *  
{@code cache} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an Observable that, when first subscribed to, caches all of its items and notifications for the - * benefit of subsequent subscribers + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Replay + * @see #takeUntil(Predicate) + * @see #takeUntil(ObservableSource) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable cache() { return cacheWithInitialCapacity(16); } /** - * Returns an Observable that subscribes to this ObservableSource lazily, caches all of its events - * and replays them, in the same order as received, to all the downstream subscribers. + * Returns an {@code Observable} that subscribes to the current {@code Observable} lazily, caches all of its events + * and replays them, in the same order as received, to all the downstream observers. *

- * + * *

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

- * The operator subscribes only when the first downstream subscriber subscribes and maintains - * a single subscription towards this ObservableSource. In contrast, the operator family of {@link #replay()} + * The operator subscribes only when the first downstream observer subscribes and maintains + * a single subscription towards the current {@code Observable}. In contrast, the operator family of {@link #replay()} * that return a {@link ConnectableObservable} require an explicit call to {@link ConnectableObservable#connect()}. *

* Note: You sacrifice the ability to dispose the origin when you use the {@code cache} - * Observer so be careful not to use this Observer on ObservableSources that emit an infinite or very large number + * operator so be careful not to use this operator on {@code Observable}s that emit an infinite or very large number * of items that will use up memory. * A possible workaround is to apply `takeUntil` with a predicate or - * another source before (and perhaps after) the application of cache(). + * another source before (and perhaps after) the application of {@code cache()}. *


      * AtomicBoolean shouldStop = new AtomicBoolean();
      *
@@ -6106,22 +6653,27 @@ public final Observable cache() {
      * {@link #replay(int)} in combination with {@link ConnectableObservable#autoConnect()} or similar.
      *
      * @param initialCapacity hint for number of items to cache (for optimizing underlying data structure)
-     * @return an Observable that, when first subscribed to, caches all of its items and notifications for the
-     *         benefit of subsequent subscribers
+     * @return the new {@code Observable} instance
+     * @throws IllegalArgumentException if {@code initialCapacity} is non-positive
      * @see ReactiveX operators documentation: Replay
+     * @see #takeUntil(Predicate)
+     * @see #takeUntil(ObservableSource)
      */
     @CheckReturnValue
     @SchedulerSupport(SchedulerSupport.NONE)
+    @NonNull
     public final Observable cacheWithInitialCapacity(int initialCapacity) {
         ObjectHelper.verifyPositive(initialCapacity, "initialCapacity");
-        return RxJavaPlugins.onAssembly(new ObservableCache(this, initialCapacity));
+        return RxJavaPlugins.onAssembly(new ObservableCache<>(this, initialCapacity));
     }
 
     /**
-     * Returns an Observable that emits the items emitted by the source ObservableSource, converted to the specified
-     * type.
+     * Returns an {@code Observable} that emits the upstream items while
+     * they can be cast via {@link Class#cast(Object)} until the upstream terminates,
+     * or until the upstream signals an item which can't be cast,
+     * resulting in a {@link ClassCastException} to be signaled to the downstream.
      * 

- * + * *

*
Scheduler:
*
{@code cast} does not operate by default on a particular {@link Scheduler}.
@@ -6129,118 +6681,122 @@ public final Observable cacheWithInitialCapacity(int initialCapacity) { * * @param the output value type cast to * @param clazz - * the target class type that {@code cast} will cast the items emitted by the source ObservableSource - * into before emitting them from the resulting ObservableSource - * @return an Observable that emits each item from the source ObservableSource after converting it to the - * specified type + * the target class to use to try and cast the upstream items into + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code clazz} is {@code null} * @see ReactiveX operators documentation: Map */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable cast(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + @NonNull + public final <@NonNull U> Observable cast(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return map(Functions.castFunction(clazz)); } /** - * Collects items emitted by the finite source ObservableSource into a single mutable data structure and returns - * a Single that emits this structure. + * Collects items emitted by the finite source {@code Observable} into a single mutable data structure and returns + * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code collect} does not operate by default on a particular {@link Scheduler}.
*
* * @param the accumulator and output type - * @param initialValueSupplier + * @param initialItemSupplier * the mutable data structure that will collect the items * @param collector - * a function that accepts the {@code state} and an emitted item, and modifies {@code state} + * a function that accepts the {@code state} and an emitted item, and modifies the accumulator accordingly * accordingly - * @return a Single that emits the result of collecting the values emitted by the source ObservableSource - * into a single mutable data structure + * @return the new {@code Single} instance + * @throws NullPointerException if {@code initialItemSupplier} or {@code collector} is {@code null} * @see ReactiveX operators documentation: Reduce */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single collect(Supplier initialValueSupplier, BiConsumer collector) { - ObjectHelper.requireNonNull(initialValueSupplier, "initialValueSupplier is null"); - ObjectHelper.requireNonNull(collector, "collector is null"); - return RxJavaPlugins.onAssembly(new ObservableCollectSingle(this, initialValueSupplier, collector)); + @NonNull + public final <@NonNull U> Single collect(@NonNull Supplier initialItemSupplier, @NonNull BiConsumer collector) { + Objects.requireNonNull(initialItemSupplier, "initialItemSupplier is null"); + Objects.requireNonNull(collector, "collector is null"); + return RxJavaPlugins.onAssembly(new ObservableCollectSingle<>(this, initialItemSupplier, collector)); } /** - * Collects items emitted by the finite source ObservableSource into a single mutable data structure and returns - * a Single that emits this structure. + * Collects items emitted by the finite source {@code Observable} into a single mutable data structure and returns + * a {@link Single} that emits this structure. *

- * + * *

* This is a simplified version of {@code reduce} that does not need to return the state on each pass. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code collectInto} does not operate by default on a particular {@link Scheduler}.
*
* * @param the accumulator and output type - * @param initialValue + * @param initialItem * the mutable data structure that will collect the items * @param collector - * a function that accepts the {@code state} and an emitted item, and modifies {@code state} + * a function that accepts the {@code state} and an emitted item, and modifies the accumulator accordingly * accordingly - * @return a Single that emits the result of collecting the values emitted by the source ObservableSource - * into a single mutable data structure + * @return the new {@code Single} instance + * @throws NullPointerException if {@code initialItem} or {@code collector} is {@code null} * @see ReactiveX operators documentation: Reduce */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single collectInto(final U initialValue, BiConsumer collector) { - ObjectHelper.requireNonNull(initialValue, "initialValue is null"); - return collect(Functions.justSupplier(initialValue), collector); + @NonNull + public final <@NonNull U> Single collectInto(@NonNull U initialItem, @NonNull BiConsumer collector) { + Objects.requireNonNull(initialItem, "initialItem is null"); + return collect(Functions.justSupplier(initialItem), collector); } /** - * Transform an ObservableSource by applying a particular Transformer function to it. + * Transform the current {@code Observable} by applying a particular {@link ObservableTransformer} function to it. *

- * This method operates on the ObservableSource itself whereas {@link #lift} operates on the ObservableSource's - * Observers. + * This method operates on the {@code Observable} itself whereas {@link #lift} operates on the {@link ObservableSource}'s + * {@link Observer}s. *

- * If the operator you are creating is designed to act on the individual items emitted by a source - * ObservableSource, use {@link #lift}. If your operator is designed to transform the source ObservableSource as a whole + * If the operator you are creating is designed to act on the individual items emitted by the current + * {@code Observable}, use {@link #lift}. If your operator is designed to transform the current {@code Observable} as a whole * (for instance, by applying a particular set of existing RxJava operators to it) use {@code compose}. *

*
Scheduler:
*
{@code compose} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the output ObservableSource - * @param composer implements the function that transforms the source ObservableSource - * @return the source ObservableSource, transformed by the transformer function + * @param the value type of the output {@code ObservableSource} + * @param composer implements the function that transforms the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code composer} is {@code null} * @see RxJava wiki: Implementing Your Own Operators */ @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable compose(ObservableTransformer composer) { - return wrap(((ObservableTransformer) ObjectHelper.requireNonNull(composer, "composer is null")).apply(this)); + @NonNull + public final <@NonNull R> Observable compose(@NonNull ObservableTransformer composer) { + return wrap(((ObservableTransformer) Objects.requireNonNull(composer, "composer is null")).apply(this)); } /** - * Returns a new Observable that emits items resulting from applying a function that you supply to each item - * emitted by the source ObservableSource, where that function returns an ObservableSource, and then emitting the items - * that result from concatenating those resulting ObservableSources. + * Returns a new {@code Observable} that emits items resulting from applying a function that you supply to each item + * emitted by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then emitting the items + * that result from concatenating those returned {@code ObservableSource}s. *

- * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -6250,27 +6806,28 @@ public final Observable compose(ObservableTransformer{@code concatMap} does not operate by default on a particular {@link Scheduler}.

*
* - * @param the type of the inner ObservableSource sources and thus the output type + * @param the type of the inner {@code ObservableSource} sources and thus the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and concatenating the ObservableSources obtained from this transformation + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #concatMap(Function, int, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMap(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMap(@NonNull Function> mapper) { return concatMap(mapper, 2); } /** - * Returns a new Observable that emits items resulting from applying a function that you supply to each item - * emitted by the source ObservableSource, where that function returns an ObservableSource, and then emitting the items - * that result from concatenating those resulting ObservableSources. + * Returns a new {@code Observable} that emits items resulting from applying a function that you supply to each item + * emitted by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then emitting the items + * that result from concatenating those returned {@code ObservableSource}s. *

- * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -6280,22 +6837,24 @@ public final Observable concatMap(Function{@code concatMap} does not operate by default on a particular {@link Scheduler}.

*
* - * @param the type of the inner ObservableSource sources and thus the output type + * @param the type of the inner {@code ObservableSource} sources and thus the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource - * @param prefetch - * the number of elements to prefetch from the current Observable - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and concatenating the ObservableSources obtained from this transformation + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} + * @param bufferSize + * the number of elements expected from the current {@code Observable} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @see #concatMap(Function, int, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMap(Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); + @NonNull + public final <@NonNull R> Observable concatMap(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") T v = ((ScalarSupplier)this).get(); @@ -6304,15 +6863,15 @@ public final Observable concatMap(Function(this, mapper, prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new ObservableConcatMap<>(this, mapper, bufferSize, ErrorMode.IMMEDIATE)); } /** - * Returns a new Observable that emits items resulting from applying a function that you supply to each item - * emitted by the source ObservableSource, where that function returns an ObservableSource, and then emitting the items - * that result from concatenating those resulting ObservableSources. + * Returns a new {@code Observable} that emits items resulting from applying a function that you supply to each item + * emitted by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then emitting the items + * that result from concatenating those returned {@code ObservableSource}s. *

- * + * *

* The difference between {@link #concatMap(Function, int)} and this operator is that this operator guarantees the {@code mapper} * function is executed on the specified scheduler. @@ -6321,34 +6880,37 @@ public final Observable concatMap(Function{@code concatMap} executes the given {@code mapper} function on the provided {@link Scheduler}.

*
* - * @param the type of the inner ObservableSource sources and thus the output type + * @param the type of the inner {@code ObservableSource} sources and thus the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource - * @param prefetch - * the number of elements to prefetch from the current Observable + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} + * @param bufferSize + * the number of elements expected from the current {@code Observable} to be buffered * @param scheduler * the scheduler where the {@code mapper} function will be executed - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and concatenating the ObservableSources obtained from this transformation + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive + * @since 3.0.0 * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable concatMap(Function> mapper, int prefetch, Scheduler scheduler) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapScheduler(this, mapper, prefetch, ErrorMode.IMMEDIATE, scheduler)); + @NonNull + public final <@NonNull R> Observable concatMap(@NonNull Function> mapper, int bufferSize, @NonNull Scheduler scheduler) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapScheduler<>(this, mapper, bufferSize, ErrorMode.IMMEDIATE, scheduler)); } /** - * Maps each of the items into an ObservableSource, subscribes to them one after the other, + * Maps each of the items into an {@link ObservableSource}, subscribes to them one after the other, * one at a time and emits their values in order - * while delaying any error from either this or any of the inner ObservableSources + * while delaying any error from either this or any of the inner {@code ObservableSource}s * till all of them terminate. *

- * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -6359,23 +6921,25 @@ public final Observable concatMap(Function * * @param the result value type - * @param mapper the function that maps the items of this ObservableSource into the inner ObservableSources. - * @return the new ObservableSource instance with the concatenation behavior + * @param mapper the function that maps the items of the current {@code Observable} into the inner {@code ObservableSource}s. + * @return the new {@code Observable} instance with the concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} * @see #concatMapDelayError(Function, boolean, int, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMapDelayError(@NonNull Function> mapper) { return concatMapDelayError(mapper, true, bufferSize()); } /** - * Maps each of the items into an ObservableSource, subscribes to them one after the other, + * Maps each of the items into an {@link ObservableSource}, subscribes to them one after the other, * one at a time and emits their values in order - * while delaying any error from either this or any of the inner ObservableSources + * while delaying any error from either this or any of the inner {@code ObservableSource}s * till all of them terminate. *

- * + * *

* Note that there is no guarantee where the given {@code mapper} function will be executed; it could be on the subscribing thread, * on the upstream thread signaling the new item to be mapped or on the thread where the inner source terminates. To ensure @@ -6386,21 +6950,24 @@ public final Observable concatMapDelayError(Function * * @param the result value type - * @param mapper the function that maps the items of this ObservableSource into the inner ObservableSources. + * @param mapper the function that maps the items of the current {@code Observable} into the inner {@code ObservableSource}s. * @param tillTheEnd - * if true, all errors from the outer and inner ObservableSource sources are delayed until the end, - * if false, an error from the main source is signalled when the current ObservableSource source terminates - * @param prefetch - * the number of elements to prefetch from the current Observable - * @return the new ObservableSource instance with the concatenation behavior + * if {@code true}, all errors from the outer and inner {@code ObservableSource} sources are delayed until the end, + * if {@code false}, an error from the main source is signaled when the current {@code Observable} source terminates + * @param bufferSize + * the number of elements expected from the current {@code Observable} to be buffered + * @return the new {@code Observable} instance with the concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see #concatMapDelayError(Function, boolean, int, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapDelayError(Function> mapper, - boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); + @NonNull + public final <@NonNull R> Observable concatMapDelayError(@NonNull Function> mapper, + boolean tillTheEnd, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") T v = ((ScalarSupplier)this).get(); @@ -6409,106 +6976,114 @@ public final Observable concatMapDelayError(Function(this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); + return RxJavaPlugins.onAssembly(new ObservableConcatMap<>(this, mapper, bufferSize, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); } /** - * Maps each of the items into an ObservableSource, subscribes to them one after the other, + * Maps each of the items into an {@link ObservableSource}, subscribes to them one after the other, * one at a time and emits their values in order - * while delaying any error from either this or any of the inner ObservableSources + * while delaying any error from either this or any of the inner {@code ObservableSource}s * till all of them terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* * @param the result value type - * @param mapper the function that maps the items of this ObservableSource into the inner ObservableSources. + * @param mapper the function that maps the items of the current {@code Observable} into the inner {@code ObservableSource}s. * @param tillTheEnd - * if true, all errors from the outer and inner ObservableSource sources are delayed until the end, - * if false, an error from the main source is signalled when the current ObservableSource source terminates - * @param prefetch - * the number of elements to prefetch from the current Observable + * if {@code true}, all errors from the outer and inner {@code ObservableSource} sources are delayed until the end, + * if {@code false}, an error from the main source is signaled when the current {@code Observable} source terminates + * @param bufferSize + * the number of elements expected from the current {@code Observable} to be buffered * @param scheduler * the scheduler where the {@code mapper} function will be executed - * @return the new ObservableSource instance with the concatenation behavior + * @return the new {@code Observable} instance with the concatenation behavior + * @throws NullPointerException if {@code mapper} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see #concatMapDelayError(Function, boolean, int) * @since 3.0.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable concatMapDelayError(Function> mapper, - boolean tillTheEnd, int prefetch, Scheduler scheduler) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapScheduler(this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, scheduler)); + @NonNull + public final <@NonNull R> Observable concatMapDelayError(@NonNull Function> mapper, + boolean tillTheEnd, int bufferSize, @NonNull Scheduler scheduler) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapScheduler<>(this, mapper, bufferSize, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, scheduler)); } /** - * Maps a sequence of values into ObservableSources and concatenates these ObservableSources eagerly into a single - * ObservableSource. + * Maps a sequence of values into {@link ObservableSource}s and concatenates these {@code ObservableSource}s eagerly into a single + * {@code Observable} sequence. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them in + * current {@code Observable}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them in * order, each one after the previous one completes. *

- * + * *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of ObservableSources that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code ObservableSource}s that will be * eagerly concatenated - * @return the new ObservableSource instance with the specified concatenation behavior + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapEager(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMapEager(@NonNull Function> mapper) { return concatMapEager(mapper, Integer.MAX_VALUE, bufferSize()); } /** - * Maps a sequence of values into ObservableSources and concatenates these ObservableSources eagerly into a single - * ObservableSource. + * Maps a sequence of values into {@link ObservableSource}s and concatenates these {@code ObservableSource}s eagerly into a single + * {@code Observable} sequence. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them in + * current {@code Observable}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them in * order, each one after the previous one completes. *

- * + * *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of ObservableSources that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code ObservableSource}s that will be * eagerly concatenated - * @param maxConcurrency the maximum number of concurrent subscribed ObservableSources - * @param prefetch hints about the number of expected values from each inner ObservableSource, must be positive - * @return the new ObservableSource instance with the specified concatenation behavior + * @param maxConcurrency the maximum number of concurrent subscribed {@code ObservableSource}s + * @param bufferSize hints about the number of expected items from each inner {@code ObservableSource}, must be positive + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapEager(Function> mapper, - int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + @NonNull + public final <@NonNull R> Observable concatMapEager(@NonNull Function> mapper, + int maxConcurrency, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapEager(this, mapper, ErrorMode.IMMEDIATE, maxConcurrency, prefetch)); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapEager<>(this, mapper, ErrorMode.IMMEDIATE, maxConcurrency, bufferSize)); } /** - * Maps a sequence of values into ObservableSources and concatenates these ObservableSources eagerly into a single - * ObservableSource. + * Maps a sequence of values into {@link ObservableSource}s and concatenates these {@code ObservableSource}s eagerly into a single + * {@code Observable} sequence. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them in + * current {@code Observable}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them in * order, each one after the previous one completes. *

* @@ -6517,27 +7092,29 @@ public final Observable concatMapEager(FunctionThis method does not operate by default on a particular {@link Scheduler}.

*
* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of ObservableSources that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code ObservableSource}s that will be * eagerly concatenated * @param tillTheEnd - * if true, all errors from the outer and inner ObservableSource sources are delayed until the end, - * if false, an error from the main source is signalled when the current ObservableSource source terminates - * @return the new ObservableSource instance with the specified concatenation behavior + * if {@code true}, all errors from the outer and inner {@code ObservableSource} sources are delayed until the end, + * if {@code false}, an error from the main source is signaled when the current {@code Observable} source terminates + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapEagerDelayError(Function> mapper, + @NonNull + public final <@NonNull R> Observable concatMapEagerDelayError(@NonNull Function> mapper, boolean tillTheEnd) { return concatMapEagerDelayError(mapper, tillTheEnd, Integer.MAX_VALUE, bufferSize()); } /** - * Maps a sequence of values into ObservableSources and concatenates these ObservableSources eagerly into a single - * ObservableSource. + * Maps a sequence of values into {@link ObservableSource}s and concatenates these {@code ObservableSource}s eagerly into a single + * {@code Observable} sequence. *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source ObservableSources. The operator buffers the values emitted by these ObservableSources and then drains them in + * current {@code Observable}s. The operator buffers the values emitted by these {@code ObservableSource}s and then drains them in * order, each one after the previous one completes. *

* @@ -6546,82 +7123,90 @@ public final Observable concatMapEagerDelayError(FunctionThis method does not operate by default on a particular {@link Scheduler}. *

* @param the value type - * @param mapper the function that maps a sequence of values into a sequence of ObservableSources that will be + * @param mapper the function that maps a sequence of values into a sequence of {@code ObservableSource}s that will be * eagerly concatenated * @param tillTheEnd - * if true, exceptions from the current Observable and all the inner ObservableSources are delayed until - * all of them terminate, if false, exception from the current Observable is delayed until the - * currently running ObservableSource terminates - * @param maxConcurrency the maximum number of concurrent subscribed ObservableSources - * @param prefetch - * the number of elements to prefetch from each source ObservableSource - * @return the new ObservableSource instance with the specified concatenation behavior + * if {@code true}, exceptions from the current {@code Observable} and all the inner {@code ObservableSource}s are delayed until + * all of them terminate, if {@code false}, exception from the current {@code Observable} is delayed until the + * currently running {@code ObservableSource} terminates + * @param maxConcurrency the maximum number of concurrent subscribed {@code ObservableSource}s + * @param bufferSize + * the number of elements expected from the current {@code Observable} and each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapEagerDelayError(Function> mapper, - boolean tillTheEnd, int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + @NonNull + public final <@NonNull R> Observable concatMapEagerDelayError(@NonNull Function> mapper, + boolean tillTheEnd, int maxConcurrency, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapEager(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, maxConcurrency, prefetch)); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapEager<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, maxConcurrency, bufferSize)); } /** - * Maps each element of the upstream Observable into CompletableSources, subscribes to them one at a time in - * order and waits until the upstream and all CompletableSources complete. + * Maps each element of the current {@code Observable} into {@link CompletableSource}s, subscribes to them one at a time in + * order and waits until the upstream and all {@code CompletableSource}s complete. *

- * + * *

*
Scheduler:
*
{@code concatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.6 - experimental * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns a CompletableSource - * @return a Completable that signals {@code onComplete} when the upstream and all CompletableSources complete + * a function that, when applied to an item emitted by the current {@code Observable}, returns a {@code CompletableSource} + * @return the new {@link Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable concatMapCompletable(Function mapper) { + @NonNull + public final Completable concatMapCompletable(@NonNull Function mapper) { return concatMapCompletable(mapper, 2); } /** - * Maps each element of the upstream Observable into CompletableSources, subscribes to them one at a time in - * order and waits until the upstream and all CompletableSources complete. + * Maps each element of the current {@code Observable} into {@link CompletableSource}s, subscribes to them one at a time in + * order and waits until the upstream and all {@code CompletableSource}s complete. *

- * + * *

*
Scheduler:
*
{@code concatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.6 - experimental * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns a CompletableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns a {@code CompletableSource} * * @param capacityHint - * the number of upstream items expected to be buffered until the current CompletableSource, mapped from + * the number of upstream items expected to be buffered until the current {@code CompletableSource}, mapped from * the current item, completes. - * @return a Completable that signals {@code onComplete} when the upstream and all CompletableSources complete + * @return the new {@link Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable concatMapCompletable(Function mapper, int capacityHint) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + @NonNull + public final Completable concatMapCompletable(@NonNull Function mapper, int capacityHint) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(capacityHint, "capacityHint"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapCompletable(this, mapper, ErrorMode.IMMEDIATE, capacityHint)); + return RxJavaPlugins.onAssembly(new ObservableConcatMapCompletable<>(this, mapper, ErrorMode.IMMEDIATE, capacityHint)); } /** * Maps the upstream items into {@link CompletableSource}s and subscribes to them one after the - * other terminates, delaying all errors till both this {@code Observable} and all + * other terminates, delaying all errors till both the current {@code Observable} and all * inner {@code CompletableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6630,22 +7215,24 @@ public final Completable concatMapCompletable(Function mapper) { + @NonNull + public final Completable concatMapCompletableDelayError(@NonNull Function mapper) { return concatMapCompletableDelayError(mapper, true, 2); } /** * Maps the upstream items into {@link CompletableSource}s and subscribes to them one after the - * other terminates, optionally delaying all errors till both this {@code Observable} and all + * other terminates, optionally delaying all errors till both the current {@code Observable} and all * inner {@code CompletableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6654,28 +7241,30 @@ public final Completable concatMapCompletableDelayError(Function mapper, boolean tillTheEnd) { + @NonNull + public final Completable concatMapCompletableDelayError(@NonNull Function mapper, boolean tillTheEnd) { return concatMapCompletableDelayError(mapper, tillTheEnd, 2); } /** * Maps the upstream items into {@link CompletableSource}s and subscribes to them one after the - * other terminates, optionally delaying all errors till both this {@code Observable} and all + * other terminates, optionally delaying all errors till both the current {@code Observable} and all * inner {@code CompletableSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6684,58 +7273,32 @@ public final Completable concatMapCompletableDelayError(Function mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapCompletable(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); - } - - /** - * Returns an Observable that concatenate each item emitted by the source ObservableSource with the values in an - * Iterable corresponding to that item that is generated by a selector. - *

- * - * - *

- *
Scheduler:
- *
{@code concatMapIterable} does not operate by default on a particular {@link Scheduler}.
- *
- * - * @param - * the type of item emitted by the resulting ObservableSource - * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source ObservableSource - * @return an Observable that emits the results of concatenating the items emitted by the source ObservableSource with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} - * @see ReactiveX operators documentation: FlatMap - */ - @CheckReturnValue - @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapIterable(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableFlattenIterable(this, mapper)); + @NonNull + public final Completable concatMapCompletableDelayError(@NonNull Function mapper, boolean tillTheEnd, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapCompletable<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, bufferSize)); } /** - * Returns an Observable that concatenate each item emitted by the source ObservableSource with the values in an - * Iterable corresponding to that item that is generated by a selector. + * Returns an {@code Observable} that concatenate each item emitted by the current {@code Observable} with the values in an + * {@link Iterable} corresponding to that item that is generated by a selector. *

* * @@ -6745,30 +7308,28 @@ public final Observable concatMapIterable(final Function * * @param - * the type of item emitted by the resulting ObservableSource + * the type of item emitted by the resulting {@code Observable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source ObservableSource - * @param prefetch - * the number of elements to prefetch from the current Observable - * @return an Observable that emits the results of concatenating the items emitted by the source ObservableSource with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatMapIterable(final Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return concatMap(ObservableInternalHelper.flatMapIntoIterable(mapper), prefetch); + @NonNull + public final <@NonNull U> Observable concatMapIterable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableFlattenIterable<>(this, mapper)); } /** * Maps the upstream items into {@link MaybeSource}s and subscribes to them one after the * other succeeds or completes, emits their success value if available or terminates immediately if - * either this {@code Observable} or the current inner {@code MaybeSource} fail. + * either the current {@code Observable} or the current inner {@code MaybeSource} fail. *

- * + * *

*
Scheduler:
*
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -6778,23 +7339,25 @@ public final Observable concatMapIterable(final Function Observable concatMapMaybe(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMapMaybe(@NonNull Function> mapper) { return concatMapMaybe(mapper, 2); } /** * Maps the upstream items into {@link MaybeSource}s and subscribes to them one after the * other succeeds or completes, emits their success value if available or terminates immediately if - * either this {@code Observable} or the current inner {@code MaybeSource} fail. + * either the current {@code Observable} or the current inner {@code MaybeSource} fail. *

- * + * *

*
Scheduler:
*
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
@@ -6804,29 +7367,30 @@ public final Observable concatMapMaybe(Function Observable concatMapMaybe(Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapMaybe(this, mapper, ErrorMode.IMMEDIATE, prefetch)); + @NonNull + public final <@NonNull R> Observable concatMapMaybe(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapMaybe<>(this, mapper, ErrorMode.IMMEDIATE, bufferSize)); } /** * Maps the upstream items into {@link MaybeSource}s and subscribes to them one after the * other terminates, emits their success value if available and delaying all errors - * till both this {@code Observable} and all inner {@code MaybeSource}s terminate. + * till both the current {@code Observable} and all inner {@code MaybeSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6836,23 +7400,25 @@ public final Observable concatMapMaybe(Function Observable concatMapMaybeDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMapMaybeDelayError(@NonNull Function> mapper) { return concatMapMaybeDelayError(mapper, true, 2); } /** * Maps the upstream items into {@link MaybeSource}s and subscribes to them one after the * other terminates, emits their success value if available and optionally delaying all errors - * till both this {@code Observable} and all inner {@code MaybeSource}s terminate. + * till both the current {@code Observable} and all inner {@code MaybeSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6862,29 +7428,31 @@ public final Observable concatMapMaybeDelayError(Function Observable concatMapMaybeDelayError(Function> mapper, boolean tillTheEnd) { + @NonNull + public final <@NonNull R> Observable concatMapMaybeDelayError(@NonNull Function> mapper, boolean tillTheEnd) { return concatMapMaybeDelayError(mapper, tillTheEnd, 2); } /** * Maps the upstream items into {@link MaybeSource}s and subscribes to them one after the * other terminates, emits their success value if available and optionally delaying all errors - * till both this {@code Observable} and all inner {@code MaybeSource}s terminate. + * till both the current {@code Observable} and all inner {@code MaybeSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6894,34 +7462,35 @@ public final Observable concatMapMaybeDelayError(Function Observable concatMapMaybeDelayError(Function> mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapMaybe(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); + @NonNull + public final <@NonNull R> Observable concatMapMaybeDelayError(@NonNull Function> mapper, boolean tillTheEnd, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapMaybe<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, bufferSize)); } /** * Maps the upstream items into {@link SingleSource}s and subscribes to them one after the * other succeeds, emits their success values or terminates immediately if - * either this {@code Observable} or the current inner {@code SingleSource} fail. + * either the current {@code Observable} or the current inner {@code SingleSource} fail. *

- * + * *

*
Scheduler:
*
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
@@ -6931,23 +7500,25 @@ public final Observable concatMapMaybeDelayError(Function Observable concatMapSingle(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMapSingle(@NonNull Function> mapper) { return concatMapSingle(mapper, 2); } /** * Maps the upstream items into {@link SingleSource}s and subscribes to them one after the * other succeeds, emits their success values or terminates immediately if - * either this {@code Observable} or the current inner {@code SingleSource} fail. + * either the current {@code Observable} or the current inner {@code SingleSource} fail. *

- * + * *

*
Scheduler:
*
{@code concatMapSingle} does not operate by default on a particular {@link Scheduler}.
@@ -6957,29 +7528,30 @@ public final Observable concatMapSingle(Function Observable concatMapSingle(Function> mapper, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapSingle(this, mapper, ErrorMode.IMMEDIATE, prefetch)); + @NonNull + public final <@NonNull R> Observable concatMapSingle(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapSingle<>(this, mapper, ErrorMode.IMMEDIATE, bufferSize)); } /** * Maps the upstream items into {@link SingleSource}s and subscribes to them one after the * other succeeds or fails, emits their success values and delays all errors - * till both this {@code Observable} and all inner {@code SingleSource}s terminate. + * till both the current {@code Observable} and all inner {@code SingleSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -6989,23 +7561,25 @@ public final Observable concatMapSingle(Function Observable concatMapSingleDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Observable concatMapSingleDelayError(@NonNull Function> mapper) { return concatMapSingleDelayError(mapper, true, 2); } /** * Maps the upstream items into {@link SingleSource}s and subscribes to them one after the * other succeeds or fails, emits their success values and optionally delays all errors - * till both this {@code Observable} and all inner {@code SingleSource}s terminate. + * till both the current {@code Observable} and all inner {@code SingleSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7015,29 +7589,31 @@ public final Observable concatMapSingleDelayError(Function Observable concatMapSingleDelayError(Function> mapper, boolean tillTheEnd) { + @NonNull + public final <@NonNull R> Observable concatMapSingleDelayError(@NonNull Function> mapper, boolean tillTheEnd) { return concatMapSingleDelayError(mapper, tillTheEnd, 2); } /** * Maps the upstream items into {@link SingleSource}s and subscribes to them one after the * other succeeds or fails, emits their success values and optionally delays errors - * till both this {@code Observable} and all inner {@code SingleSource}s terminate. + * till both the current {@code Observable} and all inner {@code SingleSource}s terminate. *

- * + * *

*
Scheduler:
*
{@code concatMapSingleDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -7047,162 +7623,172 @@ public final Observable concatMapSingleDelayError(Function Observable concatMapSingleDelayError(Function> mapper, boolean tillTheEnd, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ObservableConcatMapSingle(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, prefetch)); + @NonNull + public final <@NonNull R> Observable concatMapSingleDelayError(@NonNull Function> mapper, boolean tillTheEnd, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapSingle<>(this, mapper, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY, bufferSize)); } /** - * Returns an Observable that emits the items emitted from the current ObservableSource, then the next, one after - * the other, without interleaving them. + * Returns an {@code Observable} that first emits the items emitted from the current {@code Observable}, then items + * from the {@code other} {@link ObservableSource} without interleaving them. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * an ObservableSource to be concatenated after the current - * @return an Observable that emits items emitted by the two source ObservableSources, one after the other, - * without interleaving them + * an {@code ObservableSource} to be concatenated after the current + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Concat */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable concatWith(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + @NonNull + public final Observable concatWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); return concat(this, other); } /** - * Returns an {@code Observable} that emits the items from this {@code Observable} followed by the success item or error event - * of the other {@link SingleSource}. + * Returns an {@code Observable} that emits the items from the current {@code Observable} followed by the success item or error event + * of the {@code other} {@link SingleSource}. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental - * @param other the SingleSource whose signal should be emitted after this {@code Observable} completes normally. - * @return the new Observable instance + * @param other the {@code SingleSource} whose signal should be emitted after the current {@code Observable} completes normally. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable concatWith(@NonNull SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatWithSingle(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatWithSingle<>(this, other)); } /** - * Returns an {@code Observable} that emits the items from this {@code Observable} followed by the success item or terminal events + * Returns an {@code Observable} that emits the items from the current {@code Observable} followed by the success item or terminal events * of the other {@link MaybeSource}. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental - * @param other the MaybeSource whose signal should be emitted after this Observable completes normally. - * @return the new Observable instance + * @param other the {@code MaybeSource} whose signal should be emitted after the current {@code Observable} completes normally. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable concatWith(@NonNull MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatWithMaybe(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatWithMaybe<>(this, other)); } /** - * Returns an {@code Observable} that emits items from this {@code Observable} and when it completes normally, the + * Returns an {@code Observable} that emits items from the current {@code Observable} and when it completes normally, the * other {@link CompletableSource} is subscribed to and the returned {@code Observable} emits its terminal events. *

- * + * *

*
Scheduler:
*
{@code concatWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental * @param other the {@code CompletableSource} to subscribe to once the current {@code Observable} completes normally - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable concatWith(@NonNull CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatWithCompletable(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatWithCompletable<>(this, other)); } /** - * Returns a Single that emits a Boolean that indicates whether the source ObservableSource emitted a + * Returns a {@link Single} that emits a {@link Boolean} that indicates whether the current {@code Observable} emitted a * specified item. *

- * + * *

*
Scheduler:
*
{@code contains} does not operate by default on a particular {@link Scheduler}.
*
* - * @param element - * the item to search for in the emissions from the source ObservableSource - * @return a Single that emits {@code true} if the specified item is emitted by the source ObservableSource, - * or {@code false} if the source ObservableSource completes without emitting that item + * @param item + * the item to search for in the emissions from the current {@code Observable} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single contains(final Object element) { - ObjectHelper.requireNonNull(element, "element is null"); - return any(Functions.equalsWith(element)); + @NonNull + public final Single contains(@NonNull Object item) { + Objects.requireNonNull(item, "item is null"); + return any(Functions.equalsWith(item)); } /** - * Returns a Single that counts the total number of items emitted by the source ObservableSource and emits - * this count as a 64-bit Long. + * Returns a {@link Single} that counts the total number of items emitted by the current {@code Observable} and emits + * this count as a 64-bit {@link Long}. *

- * + * *

*
Scheduler:
*
{@code count} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a single item: the number of items emitted by the source ObservableSource as a - * 64-bit Long item + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Count */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single count() { - return RxJavaPlugins.onAssembly(new ObservableCountSingle(this)); + return RxJavaPlugins.onAssembly(new ObservableCountSingle<>(this)); } /** - * Returns an Observable that mirrors the source ObservableSource, except that it drops items emitted by the - * source ObservableSource that are followed by another item within a computed debounce duration. + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by another item within a computed debounce duration + * denoted by an item emission or completion from a generated inner {@link ObservableSource} for that original item. *

- * + * *

* The delivery of the item happens on the thread of the first {@code onNext} or {@code onComplete} * signal of the generated {@code ObservableSource} sequence, @@ -7218,30 +7804,31 @@ public final Single count() { * * @param * the debounce value type (ignored) - * @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 ObservableSource that are followed by another item - * within a computed debounce duration + * @param debounceIndicator + * function to return a sequence that indicates the throttle duration for each item via its own emission or completion + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code debounceIndicator} is {@code null} * @see ReactiveX operators documentation: Debounce */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable debounce(Function> debounceSelector) { - ObjectHelper.requireNonNull(debounceSelector, "debounceSelector is null"); - return RxJavaPlugins.onAssembly(new ObservableDebounce(this, debounceSelector)); + @NonNull + public final <@NonNull U> Observable debounce(@NonNull Function> debounceIndicator) { + Objects.requireNonNull(debounceIndicator, "debounceIndicator is null"); + return RxJavaPlugins.onAssembly(new ObservableDebounce<>(this, debounceIndicator)); } /** - * Returns an Observable that mirrors the source ObservableSource, except that it drops items emitted by the - * source ObservableSource that are followed by newer items before a timeout value expires. The timer resets on + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by newer items before a timeout value expires. The timer resets on * each emission. *

- * Note: If items keep being emitted by the source ObservableSource faster than the timeout then no items - * will be emitted by the resulting ObservableSource. + * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items + * will be emitted by the resulting {@code Observable}. *

- * + * *

- * Delivery of the item after the grace period happens on the {@code computation} {@code Scheduler}'s + * Delivery of the item after the grace period happens on the {@code computation} {@link Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the * {@code Worker}'s task to get disposed, which may also interrupt any downstream blocking operation * (yielding an {@code InterruptedException}). It is recommended processing items @@ -7249,35 +7836,36 @@ public final Observable debounce(Function *

Scheduler:
- *
{@code debounce} operates by default on the {@code computation} {@link Scheduler}.
+ *
{@code debounce} operates by default on the {@code computation} {@code Scheduler}.
*
* * @param timeout - * the length of the window of time that must pass after the emission of an item from the source - * ObservableSource in which that ObservableSource emits no items in order for the item to be emitted by the - * resulting ObservableSource + * the length of the window of time that must pass after the emission of an item from the current + * {@code Observable} in which the {@code Observable} emits no items in order for the item to be emitted by the + * resulting {@code Observable} * @param unit * the unit of time for the specified {@code timeout} - * @return an Observable that filters out items from the source ObservableSource that are too quickly followed by - * newer items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Debounce * @see #throttleWithTimeout(long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable debounce(long timeout, TimeUnit unit) { + @NonNull + public final Observable debounce(long timeout, @NonNull TimeUnit unit) { return debounce(timeout, unit, Schedulers.computation()); } /** - * Returns an Observable that mirrors the source ObservableSource, except that it drops items emitted by the - * source ObservableSource that are followed by newer items before a timeout value expires on a specified - * Scheduler. The timer resets on each emission. + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission. *

- * Note: If items keep being emitted by the source ObservableSource faster than the timeout then no items - * will be emitted by the resulting ObservableSource. + * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items + * will be emitted by the resulting {@code Observable}. *

- * + * *

* Delivery of the item after the grace period happens on the given {@code Scheduler}'s * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the @@ -7287,61 +7875,111 @@ public final Observable debounce(long timeout, TimeUnit unit) { * {@code debounce} itself. *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout - * the time each item has to be "the most recent" of those emitted by the source ObservableSource to + * the time each item has to be "the most recent" of those emitted by the current {@code Observable} to * ensure that it's not dropped * @param unit * the unit of time for the specified {@code timeout} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle the timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each * item - * @return an Observable that filters out items from the source ObservableSource that are too quickly followed by - * newer items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Debounce * @see #throttleWithTimeout(long, TimeUnit, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable debounce(long timeout, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableDebounceTimed(this, timeout, unit, scheduler)); + @NonNull + public final Observable debounce(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableDebounceTimed<>(this, timeout, unit, scheduler, null)); + } + + /** + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission. + *

+ * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items + * will be emitted by the resulting {@code Observable}. + *

+ * + *

+ * Delivery of the item after the grace period happens on the given {@code Scheduler}'s + * {@code Worker} which if takes too long, a newer item may arrive from the upstream, causing the + * {@code Worker}'s task to get disposed, which may also interrupt any downstream blocking operation + * (yielding an {@code InterruptedException}). It is recommended processing items + * that may take long time to be moved to another thread via {@link #observeOn} applied after + * {@code debounce} itself. + *

+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param timeout + * the time each item has to be "the most recent" of those emitted by the current {@code Observable} to + * ensure that it's not dropped + * @param unit + * the unit of time for the specified {@code timeout} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each + * item + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} } or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Debounce + * @see #throttleWithTimeout(long, TimeUnit, Scheduler, Consumer) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Observable debounce(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new ObservableDebounceTimed<>(this, timeout, unit, scheduler, onDropped)); } /** - * Returns an Observable that emits the items emitted by the source ObservableSource or a specified default item - * if the source ObservableSource is empty. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} or a specified default item + * if the current {@code Observable} is empty. *

- * + * *

*
Scheduler:
*
{@code defaultIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the item to emit if the source ObservableSource emits no items - * @return an Observable that emits either the specified default item if the source ObservableSource emits no - * items, or the items emitted by the source ObservableSource + * the item to emit if the current {@code Observable} emits no items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: DefaultIfEmpty */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable defaultIfEmpty(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); + @NonNull + public final Observable defaultIfEmpty(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); return switchIfEmpty(just(defaultItem)); } /** - * Returns an Observable that delays the emissions of the source ObservableSource via another ObservableSource on a - * per-item basis. + * Returns an {@code Observable} that delays the emissions of the current {@code Observable} via + * a per-item derived {@link ObservableSource}'s item emission or termination, on a per source item basis. *

- * + * *

- * Note: the resulting ObservableSource will immediately propagate any {@code onError} notification - * from the source ObservableSource. + * Note: the resulting {@code Observable} will immediately propagate any {@code onError} notification + * from the current {@code Observable}. *

*
Scheduler:
*
This version of {@code delay} does not operate by default on a particular {@link Scheduler}.
@@ -7349,134 +7987,146 @@ public final Observable defaultIfEmpty(T defaultItem) { * * @param * the item delay value type (ignored) - * @param itemDelay - * a function that returns an ObservableSource for each item emitted by the source ObservableSource, which is - * then used to delay the emission of that item by the resulting ObservableSource until the ObservableSource + * @param itemDelayIndicator + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable}, which is + * then used to delay the emission of that item by the resulting {@code Observable} until the {@code ObservableSource} * returned from {@code itemDelay} emits an item - * @return an Observable that delays the emissions of the source ObservableSource via another ObservableSource on a - * per-item basis + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code itemDelayIndicator} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable delay(final Function> itemDelay) { - ObjectHelper.requireNonNull(itemDelay, "itemDelay is null"); - return flatMap(ObservableInternalHelper.itemDelay(itemDelay)); + @NonNull + public final <@NonNull U> Observable delay(@NonNull Function> itemDelayIndicator) { + Objects.requireNonNull(itemDelayIndicator, "itemDelayIndicator is null"); + return flatMap(ObservableInternalHelper.itemDelay(itemDelayIndicator)); } /** - * Returns an Observable that emits the items emitted by the source ObservableSource shifted forward in time by a - * specified delay. Error notifications from the source ObservableSource are not delayed. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a + * specified delay. An error notification from the current {@code Observable} is not delayed. *

- * + * *

*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the {@link TimeUnit} in which {@code period} is defined - * @return the source ObservableSource shifted in time by the specified delay + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay + * @see #delay(long, TimeUnit, boolean) + * @see #delay(long, TimeUnit, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable delay(long delay, TimeUnit unit) { - return delay(delay, unit, Schedulers.computation(), false); + @NonNull + public final Observable delay(long time, @NonNull TimeUnit unit) { + return delay(time, unit, Schedulers.computation(), false); } /** - * Returns an Observable that emits the items emitted by the source ObservableSource shifted forward in time by a - * specified delay. If {@code delayError} is true, error notifications will also be delayed. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a + * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Scheduler:
*
This version of {@code delay} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param delayError - * if true, the upstream exception is signalled with the given delay, after all preceding normal elements, - * if false, the upstream exception is signalled immediately - * @return the source ObservableSource shifted in time by the specified delay + * if {@code true}, the upstream exception is signaled with the given delay, after all preceding normal elements, + * if {@code false}, the upstream exception is signaled immediately + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay + * @see #delay(long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable delay(long delay, TimeUnit unit, boolean delayError) { - return delay(delay, unit, Schedulers.computation(), delayError); + @NonNull + public final Observable delay(long time, @NonNull TimeUnit unit, boolean delayError) { + return delay(time, unit, Schedulers.computation(), delayError); } /** - * Returns an Observable that emits the items emitted by the source ObservableSource shifted forward in time by a - * specified delay. Error notifications from the source ObservableSource are not delayed. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a + * specified delay. An error notification from the current {@code Observable} is not delayed. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the time unit of {@code delay} * @param scheduler - * the {@link Scheduler} to use for delaying - * @return the source ObservableSource shifted in time by the specified delay + * the {@code Scheduler} to use for delaying + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable delay(long delay, TimeUnit unit, Scheduler scheduler) { - return delay(delay, unit, scheduler, false); + @NonNull + public final Observable delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delay(time, unit, scheduler, false); } /** - * Returns an Observable that emits the items emitted by the source ObservableSource shifted forward in time by a - * specified delay. If {@code delayError} is true, error notifications will also be delayed. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} shifted forward in time by a + * specified delay. If {@code delayError} is {@code true}, error notifications will also be delayed. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* - * @param delay + * @param time * the delay to shift the source by * @param unit * the time unit of {@code delay} * @param scheduler - * the {@link Scheduler} to use for delaying + * the {@code Scheduler} to use for delaying * @param delayError - * if true, the upstream exception is signalled with the given delay, after all preceding normal elements, - * if false, the upstream exception is signalled immediately - * @return the source ObservableSource shifted in time by the specified delay + * if {@code true}, the upstream exception is signaled with the given delay, after all preceding normal elements, + * if {@code false}, the upstream exception is signaled immediately + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable delay(long delay, TimeUnit unit, Scheduler scheduler, boolean delayError) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final Observable delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableDelay(this, delay, unit, scheduler, delayError)); + return RxJavaPlugins.onAssembly(new ObservableDelay<>(this, time, unit, scheduler, delayError)); } /** - * Returns an Observable that delays the subscription to and emissions from the source ObservableSource via another - * ObservableSource on a per-item basis. + * Returns an {@code Observable} that delays the subscription to and emissions from the current {@code Observable} via + * {@link ObservableSource}s for the subscription itself and on a per-item basis. *

- * + * *

- * Note: the resulting ObservableSource will immediately propagate any {@code onError} notification - * from the source ObservableSource. + * Note: the resulting {@code Observable} will immediately propagate any {@code onError} notification + * from the current {@code Observable}. *

*
Scheduler:
*
This version of {@code delay} does not operate by default on a particular {@link Scheduler}.
@@ -7486,102 +8136,107 @@ public final Observable delay(long delay, TimeUnit unit, Scheduler scheduler, * the subscription delay value type (ignored) * @param * the item delay value type (ignored) - * @param subscriptionDelay - * a function that returns an ObservableSource that triggers the subscription to the source ObservableSource + * @param subscriptionIndicator + * a function that returns an {@code ObservableSource} that triggers the subscription to the current {@code Observable} * once it emits any item - * @param itemDelay - * a function that returns an ObservableSource for each item emitted by the source ObservableSource, which is - * then used to delay the emission of that item by the resulting ObservableSource until the ObservableSource + * @param itemDelayIndicator + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable}, which is + * then used to delay the emission of that item by the resulting {@code Observable} until the {@code ObservableSource} * returned from {@code itemDelay} emits an item - * @return an Observable that delays the subscription and emissions of the source ObservableSource via another - * ObservableSource on a per-item basis + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code subscriptionIndicator} or {@code itemDelayIndicator} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable delay(ObservableSource subscriptionDelay, - Function> itemDelay) { - return delaySubscription(subscriptionDelay).delay(itemDelay); + @NonNull + public final <@NonNull U, @NonNull V> Observable delay(@NonNull ObservableSource subscriptionIndicator, + @NonNull Function> itemDelayIndicator) { + return delaySubscription(subscriptionIndicator).delay(itemDelayIndicator); } /** - * Returns an Observable that delays the subscription to this Observable - * until the other Observable emits an element or completes normally. + * Returns an {@code Observable} that delays the subscription to the current {@code Observable} + * until the other {@link ObservableSource} emits an element or completes normally. *

- * + * *

*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the other Observable, irrelevant - * @param other the other Observable that should trigger the subscription - * to this Observable. - * @return an Observable that delays the subscription to this Observable - * until the other Observable emits an element or completes normally. + * @param the value type of the other {@code Observable}, irrelevant + * @param subscriptionIndicator the other {@code ObservableSource} that should trigger the subscription + * to the current {@code Observable}. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable delaySubscription(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableDelaySubscriptionOther(this, other)); + @NonNull + public final <@NonNull U> Observable delaySubscription(@NonNull ObservableSource subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new ObservableDelaySubscriptionOther<>(this, subscriptionIndicator)); } /** - * Returns an Observable that delays the subscription to the source ObservableSource by a given amount of time. + * Returns an {@code Observable} that delays the subscription to the current {@code Observable} by a given amount of time. *

- * + * *

*
Scheduler:
*
This version of {@code delaySubscription} operates by default on the {@code computation} {@link Scheduler}.
*
* - * @param delay + * @param time * the time to delay the subscription * @param unit * the time unit of {@code delay} - * @return an Observable that delays the subscription to the source ObservableSource by the given amount + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable delaySubscription(long delay, TimeUnit unit) { - return delaySubscription(delay, unit, Schedulers.computation()); + @NonNull + public final Observable delaySubscription(long time, @NonNull TimeUnit unit) { + return delaySubscription(time, unit, Schedulers.computation()); } /** - * Returns an Observable that delays the subscription to the source ObservableSource by a given amount of time, - * both waiting and subscribing on a given Scheduler. + * Returns an {@code Observable} that delays the subscription to the current {@code Observable} by a given amount of time, + * both waiting and subscribing on a given {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* - * @param delay + * @param time * the time to delay the subscription * @param unit * the time unit of {@code delay} * @param scheduler - * the Scheduler on which the waiting and subscription will happen - * @return an Observable that delays the subscription to the source ObservableSource by a given - * amount, waiting and subscribing on the given Scheduler + * the {@code Scheduler} on which the waiting and subscription will happen + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Delay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable delaySubscription(long delay, TimeUnit unit, Scheduler scheduler) { - return delaySubscription(timer(delay, unit, scheduler)); + @NonNull + public final Observable delaySubscription(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return delaySubscription(timer(time, unit, scheduler)); } /** - * Returns an Observable that reverses the effect of {@link #materialize materialize} by transforming the + * Returns an {@code Observable} that reverses the effect of {@link #materialize materialize} by transforming the * {@link Notification} objects extracted from the source items via a selector function - * into their respective {@code Observer} signal types. + * into their respective {@link Observer} signal types. *

- * + * *

* The intended use of the {@code selector} function is to perform a * type-safe identity mapping (see example) on a source that is already of type @@ -7591,7 +8246,7 @@ public final Observable delaySubscription(long delay, TimeUnit unit, Schedule *

* When the upstream signals an {@link Notification#createOnError(Throwable) onError} or * {@link Notification#createOnComplete() onComplete} item, the - * returned Observable disposes of the flow and terminates with that type of terminal event: + * returned {@code Observable} disposes of the flow and terminates with that type of terminal event: *


      * Observable.just(createOnNext(1), createOnComplete(), createOnNext(2))
      * .doOnDispose(() -> System.out.println("Disposed!"));
@@ -7616,37 +8271,38 @@ public final Observable delaySubscription(long delay, TimeUnit unit, Schedule
      * 

History: 2.2.4 - experimental * * @param the output value type - * @param selector function that returns the upstream item and should return a Notification to signal + * @param selector function that returns the upstream item and should return a {@code Notification} to signal * the corresponding {@code Observer} event to the downstream. - * @return an Observable that emits the items and notifications embedded in the {@link Notification} objects - * selected from the items emitted by the source ObservableSource + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} is {@code null} * @see ReactiveX operators documentation: Dematerialize * @since 3.0.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable dematerialize(Function> selector) { - ObjectHelper.requireNonNull(selector, "selector is null"); - return RxJavaPlugins.onAssembly(new ObservableDematerialize(this, selector)); + @NonNull + public final <@NonNull R> Observable dematerialize(@NonNull Function> selector) { + Objects.requireNonNull(selector, "selector is null"); + return RxJavaPlugins.onAssembly(new ObservableDematerialize<>(this, selector)); } /** - * Returns an Observable that emits all items emitted by the source ObservableSource that are distinct + * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct * based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} * and {@link Object#hashCode()} to provide meaningful comparison between items as the default Java * implementation only considers reference equivalence. *

- * By default, {@code distinct()} uses an internal {@link java.util.HashSet} per Observer to remember + * By default, {@code distinct()} uses an internal {@link HashSet} per {@link Observer} to remember * previously seen items and uses {@link java.util.Set#add(Object)} returning {@code false} as the * indicator for duplicates. *

* Note that this internal {@code HashSet} may grow unbounded as items won't be removed from it by * the operator. Therefore, using very long or infinite upstream (with very distinct elements) may lead - * to {@code OutOfMemoryError}. + * to {@link OutOfMemoryError}. *

* Customizing the retention policy can happen only by providing a custom {@link java.util.Collection} implementation * to the {@link #distinct(Function, Supplier)} overload. @@ -7655,36 +8311,36 @@ public final Observable dematerialize(Function *

{@code distinct} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an Observable that emits only those items emitted by the source ObservableSource that are distinct from - * each other + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Distinct * @see #distinct(Function) * @see #distinct(Function, Supplier) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable distinct() { return distinct(Functions.identity(), Functions.createHashSet()); } /** - * Returns an Observable that emits all items emitted by the source ObservableSource that are distinct according + * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct according * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} * and {@link Object#hashCode()} to provide meaningful comparison between the key objects as the default * Java implementation only considers reference equivalence. *

- * By default, {@code distinct()} uses an internal {@link java.util.HashSet} per Observer to remember + * By default, {@code distinct()} uses an internal {@link HashSet} per {@link Observer} to remember * previously seen keys and uses {@link java.util.Set#add(Object)} returning {@code false} as the * indicator for duplicates. *

* Note that this internal {@code HashSet} may grow unbounded as keys won't be removed from it by * the operator. Therefore, using very long or infinite upstream (with very distinct keys) may lead - * to {@code OutOfMemoryError}. + * to {@link OutOfMemoryError}. *

* Customizing the retention policy can happen only by providing a custom {@link java.util.Collection} implementation * to the {@link #distinct(Function, Supplier)} overload. @@ -7697,22 +8353,24 @@ public final Observable distinct() { * @param keySelector * a function that projects an emitted item to a key value that is used to decide whether an item * is distinct from another one or not - * @return an Observable that emits those items emitted by the source ObservableSource that have distinct keys + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: Distinct * @see #distinct(Function, Supplier) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable distinct(Function keySelector) { + @NonNull + public final <@NonNull K> Observable distinct(@NonNull Function keySelector) { return distinct(keySelector, Functions.createHashSet()); } /** - * Returns an Observable that emits all items emitted by the source ObservableSource that are distinct according + * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct according * to a key selector function and based on {@link Object#equals(Object)} comparison of the objects * returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} * and {@link Object#hashCode()} to provide meaningful comparison between the key objects as @@ -7727,24 +8385,26 @@ public final Observable distinct(Function keySelector) { * a function that projects an emitted item to a key value that is used to decide whether an item * is distinct from another one or not * @param collectionSupplier - * function called for each individual Observer to return a Collection subtype for holding the extracted - * keys and whose add() method's return indicates uniqueness. - * @return an Observable that emits those items emitted by the source ObservableSource that have distinct keys + * function called for each individual {@link Observer} to return a {@link Collection} subtype for holding the extracted + * keys and whose {@code add()} method's return indicates uniqueness. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} or {@code collectionSupplier} is {@code null} * @see ReactiveX operators documentation: Distinct */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable distinct(Function keySelector, Supplier> collectionSupplier) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(collectionSupplier, "collectionSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableDistinct(this, keySelector, collectionSupplier)); + @NonNull + public final <@NonNull K> Observable distinct(@NonNull Function keySelector, @NonNull Supplier> collectionSupplier) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(collectionSupplier, "collectionSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableDistinct<>(this, keySelector, collectionSupplier)); } /** - * Returns an Observable that emits all items emitted by the source ObservableSource that are distinct from their + * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct from their * immediate predecessors based on {@link Object#equals(Object)} comparison. *

- * + * *

* It is recommended the elements' class {@code T} in the flow overrides the default {@code Object.equals()} to provide * meaningful comparison between items as the default Java implementation only considers reference equivalence. @@ -7757,7 +8417,7 @@ public final Observable distinct(Function keySelector, Supp *

* Note that if element type {@code T} in the flow is mutable, the comparison of the previous and current * item may yield unexpected results if the items are mutated externally. Common cases are mutable - * {@code CharSequence}s or {@code List}s where the objects will actually have the same + * {@link CharSequence}s or {@link List}s where the objects will actually have the same * references when they are modified and {@code distinctUntilChanged} will evaluate subsequent items as same. * To avoid such situation, it is recommended that mutable data is converted to an immutable one, * for example using {@code map(CharSequence::toString)} or {@code map(list -> Collections.unmodifiableList(new ArrayList<>(list)))}. @@ -7766,23 +8426,23 @@ public final Observable distinct(Function keySelector, Supp *

{@code distinctUntilChanged} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an Observable that emits those items from the source ObservableSource that are distinct from their - * immediate predecessors + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Distinct * @see #distinctUntilChanged(BiPredicate) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable distinctUntilChanged() { return distinctUntilChanged(Functions.identity()); } /** - * Returns an Observable that emits all items emitted by the source ObservableSource that are distinct from their + * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct from their * immediate predecessors, according to a key selector function and based on {@link Object#equals(Object)} comparison * of those objects returned by the key selector function. *

- * + * *

* It is recommended the keys' class {@code K} overrides the default {@code Object.equals()} to provide * meaningful comparison between the key objects as the default Java implementation only considers reference equivalence. @@ -7796,7 +8456,7 @@ public final Observable distinctUntilChanged() { *

* Note that if element type {@code T} in the flow is mutable, the comparison of the previous and current * item may yield unexpected results if the items are mutated externally. Common cases are mutable - * {@code CharSequence}s or {@code List}s where the objects will actually have the same + * {@link CharSequence}s or {@link List}s where the objects will actually have the same * references when they are modified and {@code distinctUntilChanged} will evaluate subsequent items as same. * To avoid such situation, it is recommended that mutable data is converted to an immutable one, * for example using {@code map(CharSequence::toString)} or {@code map(list -> Collections.unmodifiableList(new ArrayList<>(list)))}. @@ -7809,29 +8469,30 @@ public final Observable distinctUntilChanged() { * @param keySelector * a function that projects an emitted item to a key value that is used to decide whether an item * is distinct from another one or not - * @return an Observable that emits those items from the source ObservableSource whose keys are distinct from - * those of their immediate predecessors + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: Distinct */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable distinctUntilChanged(Function keySelector) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - return RxJavaPlugins.onAssembly(new ObservableDistinctUntilChanged(this, keySelector, ObjectHelper.equalsPredicate())); + @NonNull + public final <@NonNull K> Observable distinctUntilChanged(@NonNull Function keySelector) { + Objects.requireNonNull(keySelector, "keySelector is null"); + return RxJavaPlugins.onAssembly(new ObservableDistinctUntilChanged<>(this, keySelector, ObjectHelper.equalsPredicate())); } /** - * Returns an Observable that emits all items emitted by the source ObservableSource that are distinct from their + * Returns an {@code Observable} that emits all items emitted by the current {@code Observable} that are distinct from their * immediate predecessors when compared with each other via the provided comparator function. *

- * + * *

* Note that the operator always retains the latest item from upstream regardless of the comparison result * and uses it in the next comparison with the next upstream item. *

* Note that if element type {@code T} in the flow is mutable, the comparison of the previous and current * item may yield unexpected results if the items are mutated externally. Common cases are mutable - * {@code CharSequence}s or {@code List}s where the objects will actually have the same + * {@link CharSequence}s or {@link List}s where the objects will actually have the same * references when they are modified and {@code distinctUntilChanged} will evaluate subsequent items as same. * To avoid such situation, it is recommended that mutable data is converted to an immutable one, * for example using {@code map(CharSequence::toString)} or {@code map(list -> Collections.unmodifiableList(new ArrayList<>(list)))}. @@ -7841,25 +8502,27 @@ public final Observable distinctUntilChanged(Function keySe *

* * @param comparer the function that receives the previous item and the current item and is - * expected to return true if the two are equal, thus skipping the current value. - * @return an Observable that emits those items from the source ObservableSource that are distinct from their - * immediate predecessors + * expected to return {@code true} if the two are equal, thus skipping the current value. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code comparer} is {@code null} * @see ReactiveX operators documentation: Distinct * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable distinctUntilChanged(BiPredicate comparer) { - ObjectHelper.requireNonNull(comparer, "comparer is null"); - return RxJavaPlugins.onAssembly(new ObservableDistinctUntilChanged(this, Functions.identity(), comparer)); + @NonNull + public final Observable distinctUntilChanged(@NonNull BiPredicate comparer) { + Objects.requireNonNull(comparer, "comparer is null"); + return RxJavaPlugins.onAssembly(new ObservableDistinctUntilChanged<>(this, Functions.identity(), comparer)); } /** - * Calls the specified consumer with the current item after this item has been emitted to the downstream. - *

Note that the {@code onAfterNext} action is shared between subscriptions and as such + * Calls the specified {@link Consumer} with the current item after this item has been emitted to the downstream. + *

+ * Note that the {@code onAfterNext} action is shared between subscriptions and as such * should be thread-safe. *

- * + * *

*
Scheduler:
*
{@code doAfterNext} does not operate by default on a particular {@link Scheduler}.
@@ -7867,50 +8530,53 @@ public final Observable distinctUntilChanged(BiPredicateThis operator supports boundary-limited synchronous or asynchronous queue-fusion. *
*

History: 2.0.1 - experimental - * @param onAfterNext the Consumer that will be called after emitting an item from upstream to the downstream - * @return the new Observable instance + * @param onAfterNext the {@code Consumer} that will be called after emitting an item from upstream to the downstream + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onAfterNext} is {@code null} * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doAfterNext(Consumer onAfterNext) { - ObjectHelper.requireNonNull(onAfterNext, "onAfterNext is null"); - return RxJavaPlugins.onAssembly(new ObservableDoAfterNext(this, onAfterNext)); + @NonNull + public final Observable doAfterNext(@NonNull Consumer onAfterNext) { + Objects.requireNonNull(onAfterNext, "onAfterNext is null"); + return RxJavaPlugins.onAssembly(new ObservableDoAfterNext<>(this, onAfterNext)); } /** - * Registers an {@link Action} to be called when this ObservableSource invokes either + * Registers an {@link Action} to be called when the current {@code Observable} invokes either * {@link Observer#onComplete onComplete} or {@link Observer#onError onError}. *

- * + * *

*
Scheduler:
*
{@code doAfterTerminate} does not operate by default on a particular {@link Scheduler}.
*
* - * @param onFinally - * an {@link Action} to be invoked when the source ObservableSource finishes - * @return an Observable that emits the same items as the source ObservableSource, then invokes the - * {@link Action} + * @param onAfterTerminate + * an {@code Action} to be invoked after the current {@code Observable} finishes + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @see #doOnTerminate(Action) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doAfterTerminate(Action onFinally) { - ObjectHelper.requireNonNull(onFinally, "onFinally is null"); - return doOnEach(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, onFinally); + @NonNull + public final Observable doAfterTerminate(@NonNull Action onAfterTerminate) { + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); + return doOnEach(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, onAfterTerminate); } /** - * Calls the specified action after this Observable signals onError or onCompleted or gets disposed by + * Calls the specified action after the current {@code Observable} signals {@code onError} or {@code onCompleted} or gets disposed by * the downstream. *

In case of a race between a terminal event and a dispose call, the provided {@code onFinally} action * is executed once per subscription. *

Note that the {@code onFinally} action is shared between subscriptions and as such * should be thread-safe. *

- * + * *

*
Scheduler:
*
{@code doFinally} does not operate by default on a particular {@link Scheduler}.
@@ -7918,105 +8584,119 @@ public final Observable doAfterTerminate(Action onFinally) { *
This operator supports boundary-limited synchronous or asynchronous queue-fusion.
*
*

History: 2.0.1 - experimental - * @param onFinally the action called when this Observable terminates or gets disposed - * @return the new Observable instance + * @param onFinally the action called when the current {@code Observable} terminates or gets disposed + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onFinally} is {@code null} * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doFinally(Action onFinally) { - ObjectHelper.requireNonNull(onFinally, "onFinally is null"); - return RxJavaPlugins.onAssembly(new ObservableDoFinally(this, onFinally)); + @NonNull + public final Observable doFinally(@NonNull Action onFinally) { + Objects.requireNonNull(onFinally, "onFinally is null"); + return RxJavaPlugins.onAssembly(new ObservableDoFinally<>(this, onFinally)); } /** - * Calls the dispose {@code Action} if the downstream disposes the sequence. + * Calls the given shared {@link Action} if the downstream disposes the sequence. *

* The action is shared between subscriptions and thus may be called concurrently from multiple * threads; the action must be thread safe. *

* If the action throws a runtime exception, that exception is rethrown by the {@code dispose()} call, - * sometimes as a {@code CompositeException} if there were multiple exceptions along the way. + * sometimes as a {@link CompositeException} if there were multiple exceptions along the way. *

- * + * *

*
Scheduler:
*
{@code doOnDispose} does not operate by default on a particular {@link Scheduler}.
*
* * @param onDispose - * the action that gets called when the source {@code ObservableSource}'s Disposable is disposed - * @return the source {@code ObservableSource} modified so as to call this Action when appropriate - * @throws NullPointerException if onDispose is null + * the action that gets called when the current {@code Observable}'s {@link Disposable} is disposed + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onDispose} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnDispose(Action onDispose) { + @NonNull + public final Observable doOnDispose(@NonNull Action onDispose) { return doOnLifecycle(Functions.emptyConsumer(), onDispose); } /** - * Modifies the source ObservableSource so that it invokes an action when it calls {@code onComplete}. + * Returns an {@code Observable} that invokes an {@link Action} when the current {@code Observable} calls {@code onComplete}. *

- * + * *

*
Scheduler:
*
{@code doOnComplete} does not operate by default on a particular {@link Scheduler}.
*
* * @param onComplete - * the action to invoke when the source ObservableSource calls {@code onComplete} - * @return the source ObservableSource with the side-effecting behavior applied + * the action to invoke when the current {@code Observable} calls {@code onComplete} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnComplete(Action onComplete) { + @NonNull + public final Observable doOnComplete(@NonNull Action onComplete) { return doOnEach(Functions.emptyConsumer(), Functions.emptyConsumer(), onComplete, Functions.EMPTY_ACTION); } /** - * Calls the appropriate onXXX consumer (shared between all subscribers) whenever a signal with the same type - * passes through, before forwarding them to downstream. + * Calls the appropriate {@code onXXX} consumer (shared between all {@link Observer}s) whenever a signal with the same type + * passes through, before forwarding them to the downstream. *

- * + * *

*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source ObservableSource with the side-effecting behavior applied + * @param onNext the {@link Consumer} to invoke when the current {@code Observable} calls {@code onNext} + * @param onError the {@code Consumer} to invoke when the current {@code Observable} calls {@code onError} + * @param onComplete the {@link Action} to invoke when the current {@code Observable} calls {@code onComplete} + * @param onAfterTerminate the {@code Action} to invoke when the current {@code Observable} calls {@code onAfterTerminate} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onNext}, {@code onError}, {@code onComplete} or {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - private Observable doOnEach(Consumer onNext, Consumer onError, Action onComplete, Action onAfterTerminate) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); - return RxJavaPlugins.onAssembly(new ObservableDoOnEach(this, onNext, onError, onComplete, onAfterTerminate)); + @NonNull + private Observable doOnEach(@NonNull Consumer onNext, @NonNull Consumer onError, @NonNull Action onComplete, @NonNull Action onAfterTerminate) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); + return RxJavaPlugins.onAssembly(new ObservableDoOnEach<>(this, onNext, onError, onComplete, onAfterTerminate)); } /** - * Modifies the source ObservableSource so that it invokes an action for each item it emits. + * Returns an {@code Observable} that invokes a {@link Consumer} with the appropriate {@link Notification} + * object when the current {@code Observable} signals an item or terminates. *

- * + * *

*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNotification - * the action to invoke for each item emitted by the source ObservableSource - * @return the source ObservableSource with the side-effecting behavior applied + * the action to invoke for each item emitted by the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onNotification} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnEach(final Consumer> onNotification) { - ObjectHelper.requireNonNull(onNotification, "onNotification is null"); + @NonNull + public final Observable doOnEach(@NonNull Consumer> onNotification) { + Objects.requireNonNull(onNotification, "onNotification is null"); return doOnEach( Functions.notificationOnNext(onNotification), Functions.notificationOnError(onNotification), @@ -8026,29 +8706,32 @@ public final Observable doOnEach(final Consumer> onNo } /** - * Modifies the source ObservableSource so that it notifies an Observer for each item and terminal event it emits. + * Returns an {@code Observable} that forwards the items and terminal events of the current + * {@code Observable} to its {@link Observer}s and to the given shared {@code Observer} instance. *

* In case the {@code onError} of the supplied observer throws, the downstream will receive a composite * exception containing the original exception and the exception thrown by {@code onError}. If either the * {@code onNext} or the {@code onComplete} method of the supplied observer throws, the downstream will be * terminated and will receive this thrown exception. *

- * + * *

*
Scheduler:
*
{@code doOnEach} does not operate by default on a particular {@link Scheduler}.
*
* * @param observer - * the observer to be notified about onNext, onError and onComplete events on its - * respective methods before the actual downstream Observer gets notified. - * @return the source ObservableSource with the side-effecting behavior applied + * the observer to be notified about {@code onNext}, {@code onError} and {@code onComplete} events on its + * respective methods before the actual downstream {@code Observer} gets notified. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code observer} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnEach(final Observer observer) { - ObjectHelper.requireNonNull(observer, "observer is null"); + @NonNull + public final Observable doOnEach(@NonNull Observer observer) { + Objects.requireNonNull(observer, "observer is null"); return doOnEach( ObservableInternalHelper.observerOnNext(observer), ObservableInternalHelper.observerOnError(observer), @@ -8057,98 +8740,107 @@ public final Observable doOnEach(final Observer observer) { } /** - * Modifies the source ObservableSource so that it invokes an action if it calls {@code onError}. + * Calls the given {@link Consumer} with the error {@link Throwable} if the current {@code Observable} failed before forwarding it to + * the downstream. *

* In case the {@code onError} action throws, the downstream will receive a composite exception containing * the original exception and the exception thrown by {@code onError}. *

- * + * *

*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
*
* * @param onError - * the action to invoke if the source ObservableSource calls {@code onError} - * @return the source ObservableSource with the side-effecting behavior applied + * the action to invoke if the current {@code Observable} calls {@code onError} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onError} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnError(Consumer onError) { + @NonNull + public final Observable doOnError(@NonNull Consumer onError) { return doOnEach(Functions.emptyConsumer(), onError, Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Calls the appropriate onXXX method (shared between all Observer) for the lifecycle events of + * Calls the appropriate {@code onXXX} method (shared between all {@link Observer}s) for the lifecycle events of * the sequence (subscription, disposal). *

- * + * *

*
Scheduler:
*
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
*
* * @param onSubscribe - * a Consumer called with the Disposable sent via Observer.onSubscribe() + * a {@link Consumer} called with the {@link Disposable} sent via {@link Observer#onSubscribe(Disposable)} * @param onDispose - * called when the downstream disposes the Disposable via dispose() - * @return the source ObservableSource with the side-effecting behavior applied + * called when the downstream disposes the {@code Disposable} via {@code dispose()} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onSubscribe} or {@code onDispose} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnLifecycle(final Consumer onSubscribe, final Action onDispose) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - ObjectHelper.requireNonNull(onDispose, "onDispose is null"); - return RxJavaPlugins.onAssembly(new ObservableDoOnLifecycle(this, onSubscribe, onDispose)); + @NonNull + public final Observable doOnLifecycle(@NonNull Consumer onSubscribe, @NonNull Action onDispose) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + Objects.requireNonNull(onDispose, "onDispose is null"); + return RxJavaPlugins.onAssembly(new ObservableDoOnLifecycle<>(this, onSubscribe, onDispose)); } /** - * Modifies the source ObservableSource so that it invokes an action when it calls {@code onNext}. + * Calls the given {@link Consumer} with the value emitted by the current {@code Observable} before forwarding it to the downstream. *

- * + * *

*
Scheduler:
*
{@code doOnNext} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the action to invoke when the source ObservableSource calls {@code onNext} - * @return the source ObservableSource with the side-effecting behavior applied + * the action to invoke when the current {@code Observable} calls {@code onNext} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnNext(Consumer onNext) { + @NonNull + public final Observable doOnNext(@NonNull Consumer onNext) { return doOnEach(onNext, Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.EMPTY_ACTION); } /** - * Modifies the source {@code ObservableSource} so that it invokes the given action when it is subscribed from - * its subscribers. Each subscription will result in an invocation of the given action except when the - * source {@code ObservableSource} is reference counted, in which case the source {@code ObservableSource} will invoke + * Returns an {@code Observable} so that it invokes the given {@link Consumer} when the current {@code Observable} is subscribed from + * its {@link Observer}s. Each subscription will result in an invocation of the given action except when the + * current {@code Observable} is reference counted, in which case the current {@code Observable} will invoke * the given action for the first subscription. *

- * + * *

*
Scheduler:
*
{@code doOnSubscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onSubscribe - * the Consumer that gets called when an Observer subscribes to the current {@code Observable} - * @return the source {@code ObservableSource} modified so as to call this Consumer when appropriate + * the {@code Consumer} that gets called when an {@code Observer} subscribes to the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} * @see ReactiveX operators documentation: Do */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnSubscribe(Consumer onSubscribe) { + @NonNull + public final Observable doOnSubscribe(@NonNull Consumer onSubscribe) { return doOnLifecycle(onSubscribe, Functions.EMPTY_ACTION); } /** - * Modifies the source ObservableSource so that it invokes an action when it calls {@code onComplete} or + * Returns an {@code Observable} so that it invokes an action when the current {@code Observable} calls {@code onComplete} or * {@code onError}. *

* @@ -8161,23 +8853,25 @@ public final Observable doOnSubscribe(Consumer onSubscrib *

* * @param onTerminate - * the action to invoke when the source ObservableSource calls {@code onComplete} or {@code onError} - * @return the source ObservableSource with the side-effecting behavior applied + * the action to invoke when the current {@code Observable} calls {@code onComplete} or {@code onError} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @see #doAfterTerminate(Action) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable doOnTerminate(final Action onTerminate) { - ObjectHelper.requireNonNull(onTerminate, "onTerminate is null"); + @NonNull + public final Observable doOnTerminate(@NonNull Action onTerminate) { + Objects.requireNonNull(onTerminate, "onTerminate is null"); return doOnEach(Functions.emptyConsumer(), Functions.actionConsumer(onTerminate), onTerminate, Functions.EMPTY_ACTION); } /** - * Returns a Maybe that emits the single item at a specified index in a sequence of emissions from - * this Observable or completes if this Observable signals fewer elements than index. + * Returns a {@link Maybe} that emits the single item at a specified index in a sequence of emissions from + * the current {@code Observable} or completes if the current {@code Observable} signals fewer elements than index. *

* *

@@ -8187,26 +8881,26 @@ public final Observable doOnTerminate(final Action onTerminate) { * * @param index * the zero-based index of the item to retrieve - * @return a Maybe that emits a single item: the item at the specified position in the sequence of - * those emitted by the source ObservableSource + * @return the new {@code Maybe} instance * @throws IndexOutOfBoundsException - * if {@code index} is less than 0 + * if {@code index} is negative * @see ReactiveX operators documentation: ElementAt */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe elementAt(long index) { if (index < 0) { throw new IndexOutOfBoundsException("index >= 0 required but it was " + index); } - return RxJavaPlugins.onAssembly(new ObservableElementAtMaybe(this, index)); + return RxJavaPlugins.onAssembly(new ObservableElementAtMaybe<>(this, index)); } /** - * Returns a Single that emits the item found at a specified index in a sequence of emissions from - * this Observable, or a default item if that index is out of range. + * Returns a {@link Single} that emits the item found at a specified index in a sequence of emissions from + * the current {@code Observable}, or a default item if that index is out of range. *

- * + * *

*
Scheduler:
*
{@code elementAt} does not operate by default on a particular {@link Scheduler}.
@@ -8216,25 +8910,26 @@ public final Maybe elementAt(long index) { * the zero-based index of the item to retrieve * @param defaultItem * the default item - * @return a Single that emits the item at the specified position in the sequence emitted by the source - * ObservableSource, or the default item if that index is outside the bounds of the source sequence + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @throws IndexOutOfBoundsException - * if {@code index} is less than 0 + * if {@code index} is negative * @see ReactiveX operators documentation: ElementAt */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single elementAt(long index, T defaultItem) { + @NonNull + public final Single elementAt(long index, @NonNull T defaultItem) { if (index < 0) { throw new IndexOutOfBoundsException("index >= 0 required but it was " + index); } - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); - return RxJavaPlugins.onAssembly(new ObservableElementAtSingle(this, index, defaultItem)); + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new ObservableElementAtSingle<>(this, index, defaultItem)); } /** - * Returns a Single that emits the item found at a specified index in a sequence of emissions from this Observable - * or signals a {@link NoSuchElementException} if this Observable signals fewer elements than index. + * Returns a {@link Single} that emits the item found at a specified index in a sequence of emissions from the current {@code Observable} + * or signals a {@link NoSuchElementException} if the current {@code Observable} signals fewer elements than index. *

* *

@@ -8244,47 +8939,48 @@ public final Single elementAt(long index, T defaultItem) { * * @param index * the zero-based index of the item to retrieve - * @return a Single that emits the item at the specified position in the sequence emitted by the source - * ObservableSource, or the default item if that index is outside the bounds of the source sequence + * @return the new {@code Single} instance * @throws IndexOutOfBoundsException - * if {@code index} is less than 0 + * if {@code index} is negative * @see ReactiveX operators documentation: ElementAt */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single elementAtOrError(long index) { if (index < 0) { throw new IndexOutOfBoundsException("index >= 0 required but it was " + index); } - return RxJavaPlugins.onAssembly(new ObservableElementAtSingle(this, index, null)); + return RxJavaPlugins.onAssembly(new ObservableElementAtSingle<>(this, index, null)); } /** - * Filters items emitted by an ObservableSource by only emitting those that satisfy a specified predicate. + * Filters items emitted by the current {@code Observable} by only emitting those that satisfy a specified {@link Predicate}. *

- * + * *

*
Scheduler:
*
{@code filter} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates each item emitted by the source ObservableSource, returning {@code true} + * a function that evaluates each item emitted by the current {@code Observable}, returning {@code true} * if it passes the filter - * @return an Observable that emits only those items emitted by the source ObservableSource that the filter - * evaluates as {@code true} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable filter(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableFilter(this, predicate)); + @NonNull + public final Observable filter(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new ObservableFilter<>(this, predicate)); } /** - * Returns a Maybe that emits only the very first item emitted by the source ObservableSource, or - * completes if the source ObservableSource is empty. + * Returns a {@link Maybe} that emits only the very first item emitted by the current {@code Observable}, or + * completes if the current {@code Observable} is empty. *

* *

@@ -8292,85 +8988,89 @@ public final Observable filter(Predicate predicate) { *
{@code firstElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Maybe instance + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe firstElement() { return elementAt(0L); } /** - * Returns a Single that emits only the very first item emitted by the source ObservableSource, or a default item - * if the source ObservableSource completes without emitting any items. + * Returns a {@link Single} that emits only the very first item emitted by the current {@code Observable}, or a default item + * if the current {@code Observable} completes without emitting any items. *

- * + * *

*
Scheduler:
*
{@code first} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the default item to emit if the source ObservableSource doesn't emit anything - * @return the new Single instance + * the default item to emit if the current {@code Observable} doesn't emit anything + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single first(T defaultItem) { + @NonNull + public final Single first(@NonNull T defaultItem) { return elementAt(0L, defaultItem); } /** - * Returns a Single that emits only the very first item emitted by this Observable or - * signals a {@link NoSuchElementException} if this Observable is empty. + * Returns a {@link Single} that emits only the very first item emitted by the current {@code Observable} or + * signals a {@link NoSuchElementException} if the current {@code Observable} is empty. *

- * + * *

*
Scheduler:
*
{@code firstOrError} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Single instance + * @return the new {@code Single} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single firstOrError() { return elementAtOrError(0L); } /** - * Returns an Observable that emits items based on applying a function that you supply to each item emitted - * by the source ObservableSource, where that function returns an ObservableSource, and then merging those resulting - * ObservableSources and emitting the results of this merger. + * Returns an {@code Observable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then merging those returned + * {@code ObservableSource}s and emitting the results of this merger. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner ObservableSources and the output type + * @param the value type of the inner {@code ObservableSource}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and merging the results of the ObservableSources obtained from this - * transformation + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper) { + @NonNull + public final <@NonNull R> Observable flatMap(@NonNull Function> mapper) { return flatMap(mapper, false); } /** - * Returns an Observable that emits items based on applying a function that you supply to each item emitted - * by the source ObservableSource, where that function returns an ObservableSource, and then merging those resulting - * ObservableSources and emitting the results of this merger. + * Returns an {@code Observable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then merging those returned + * {@code ObservableSource}s and emitting the results of this merger. *

* *

@@ -8378,91 +9078,93 @@ public final Observable flatMap(Function{@code flatMap} does not operate by default on a particular {@link Scheduler}. *
* - * @param the value type of the inner ObservableSources and the output type + * @param the value type of the inner {@code ObservableSource}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} * @param delayErrors - * if true, exceptions from the current Observable and all inner ObservableSources are delayed until all of them terminate - * if false, the first one signalling an exception will terminate the whole sequence immediately - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and merging the results of the ObservableSources obtained from this - * transformation + * if {@code true}, exceptions from the current {@code Observable} and all inner {@code ObservableSource}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, boolean delayErrors) { + @NonNull + public final <@NonNull R> Observable flatMap(@NonNull Function> mapper, boolean delayErrors) { return flatMap(mapper, delayErrors, Integer.MAX_VALUE); } /** - * Returns an Observable that emits items based on applying a function that you supply to each item emitted - * by the source ObservableSource, where that function returns an ObservableSource, and then merging those resulting - * ObservableSources and emitting the results of this merger, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Returns an {@code Observable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then merging those returned + * {@code ObservableSource}s and emitting the results of this merger, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner ObservableSources and the output type + * @param the value type of the inner {@code ObservableSource}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Observable and all inner ObservableSources are delayed until all of them terminate - * if false, the first one signalling an exception will terminate the whole sequence immediately - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and merging the results of the ObservableSources obtained from this - * transformation + * if {@code true}, exceptions from the current {@code Observable} and all inner {@code ObservableSource}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, boolean delayErrors, int maxConcurrency) { + @NonNull + public final <@NonNull R> Observable flatMap(@NonNull Function> mapper, boolean delayErrors, int maxConcurrency) { return flatMap(mapper, delayErrors, maxConcurrency, bufferSize()); } /** - * Returns an Observable that emits items based on applying a function that you supply to each item emitted - * by the source ObservableSource, where that function returns an ObservableSource, and then merging those resulting - * ObservableSources and emitting the results of this merger, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Returns an {@code Observable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then merging those returned + * {@code ObservableSource}s and emitting the results of this merger, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner ObservableSources and the output type + * @param the value type of the inner {@code ObservableSource}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Observable and all inner ObservableSources are delayed until all of them terminate - * if false, the first one signalling an exception will terminate the whole sequence immediately + * if {@code true}, exceptions from the current {@code Observable} and all inner {@code ObservableSource}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately * @param bufferSize - * the number of elements to prefetch from each inner ObservableSource - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and merging the results of the ObservableSources obtained from this - * transformation + * the number of elements expected from each inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, + @NonNull + public final <@NonNull R> Observable flatMap(@NonNull Function> mapper, boolean delayErrors, int maxConcurrency, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @@ -8473,14 +9175,14 @@ public final Observable flatMap(Function(this, mapper, delayErrors, maxConcurrency, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableFlatMap<>(this, mapper, delayErrors, maxConcurrency, bufferSize)); } /** - * Returns an Observable that applies a function to each item emitted or notification raised by the source - * ObservableSource and then flattens the ObservableSources returned from these functions and emits the resulting items. + * Returns an {@code Observable} that applies a function to each item emitted or notification raised by the current + * {@code Observable} and then flattens the {@link ObservableSource}s returned from these functions and emits the resulting items. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -8489,35 +9191,36 @@ public final Observable flatMap(Function * the result type * @param onNextMapper - * a function that returns an ObservableSource to merge for each item emitted by the source ObservableSource + * a function that returns an {@code ObservableSource} to merge for each item emitted by the current {@code Observable} * @param onErrorMapper - * a function that returns an ObservableSource to merge for an onError notification from the source - * ObservableSource + * a function that returns an {@code ObservableSource} to merge for an {@code onError} notification from the current + * {@code Observable} * @param onCompleteSupplier - * a function that returns an ObservableSource to merge for an onComplete notification from the source - * ObservableSource - * @return an Observable that emits the results of merging the ObservableSources returned from applying the - * specified functions to the emissions and notifications of the source ObservableSource + * a function that returns an {@code ObservableSource} to merge for an {@code onComplete} notification from the current + * {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onNextMapper} or {@code onErrorMapper} or {@code onCompleteSupplier} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap( - Function> onNextMapper, - Function> onErrorMapper, - Supplier> onCompleteSupplier) { - ObjectHelper.requireNonNull(onNextMapper, "onNextMapper is null"); - ObjectHelper.requireNonNull(onErrorMapper, "onErrorMapper is null"); - ObjectHelper.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); - return merge(new ObservableMapNotification(this, onNextMapper, onErrorMapper, onCompleteSupplier)); + @NonNull + public final <@NonNull R> Observable flatMap( + @NonNull Function> onNextMapper, + @NonNull Function> onErrorMapper, + @NonNull Supplier> onCompleteSupplier) { + Objects.requireNonNull(onNextMapper, "onNextMapper is null"); + Objects.requireNonNull(onErrorMapper, "onErrorMapper is null"); + Objects.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); + return merge(new ObservableMapNotification<>(this, onNextMapper, onErrorMapper, onCompleteSupplier)); } /** - * Returns an Observable that applies a function to each item emitted or notification raised by the source - * ObservableSource and then flattens the ObservableSources returned from these functions and emits the resulting items, - * while limiting the maximum number of concurrent subscriptions to these ObservableSources. + * Returns an {@code Observable} that applies a function to each item emitted or notification raised by the current + * {@code Observable} and then flattens the {@link ObservableSource}s returned from these functions and emits the resulting items, + * while limiting the maximum number of concurrent subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -8526,280 +9229,295 @@ public final Observable flatMap( * @param * the result type * @param onNextMapper - * a function that returns an ObservableSource to merge for each item emitted by the source ObservableSource + * a function that returns an {@code ObservableSource} to merge for each item emitted by the current {@code Observable} * @param onErrorMapper - * a function that returns an ObservableSource to merge for an onError notification from the source - * ObservableSource + * a function that returns an {@code ObservableSource} to merge for an {@code onError} notification from the current + * {@code Observable} * @param onCompleteSupplier - * a function that returns an ObservableSource to merge for an onComplete notification from the source - * ObservableSource + * a function that returns an {@code ObservableSource} to merge for an {@code onComplete} notification from the current + * {@code Observable} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits the results of merging the ObservableSources returned from applying the - * specified functions to the emissions and notifications of the source ObservableSource + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code onNextMapper} or {@code onErrorMapper} or {@code onCompleteSupplier} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap( - Function> onNextMapper, - Function> onErrorMapper, - Supplier> onCompleteSupplier, + @NonNull + public final <@NonNull R> Observable flatMap( + @NonNull Function> onNextMapper, + @NonNull Function> onErrorMapper, + @NonNull Supplier> onCompleteSupplier, int maxConcurrency) { - ObjectHelper.requireNonNull(onNextMapper, "onNextMapper is null"); - ObjectHelper.requireNonNull(onErrorMapper, "onErrorMapper is null"); - ObjectHelper.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); - return merge(new ObservableMapNotification(this, onNextMapper, onErrorMapper, onCompleteSupplier), maxConcurrency); + Objects.requireNonNull(onNextMapper, "onNextMapper is null"); + Objects.requireNonNull(onErrorMapper, "onErrorMapper is null"); + Objects.requireNonNull(onCompleteSupplier, "onCompleteSupplier is null"); + return merge(new ObservableMapNotification<>(this, onNextMapper, onErrorMapper, onCompleteSupplier), maxConcurrency); } /** - * Returns an Observable that emits items based on applying a function that you supply to each item emitted - * by the source ObservableSource, where that function returns an ObservableSource, and then merging those resulting - * ObservableSources and emitting the results of this merger, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Returns an {@code Observable} that emits items based on applying a function that you supply to each item emitted + * by the current {@code Observable}, where that function returns an {@link ObservableSource}, and then merging those returned + * {@code ObservableSource}s and emitting the results of this merger, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the inner ObservableSources and the output type + * @param the value type of the inner {@code ObservableSource}s and the output type * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits the result of applying the transformation function to each item emitted - * by the source ObservableSource and merging the results of the ObservableSources obtained from this - * transformation + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, int maxConcurrency) { + @NonNull + public final <@NonNull R> Observable flatMap(@NonNull Function> mapper, int maxConcurrency) { return flatMap(mapper, false, maxConcurrency, bufferSize()); } /** - * Returns an Observable that emits the results of a specified function to the pair of values emitted by the - * source ObservableSource and a specified collection ObservableSource. + * Returns an {@code Observable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Observable} and the mapped inner {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the collection ObservableSource + * the type of items emitted by the collection {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param mapper - * a function that returns an ObservableSource for each item emitted by the source ObservableSource - * @param resultSelector - * a function that combines one item emitted by each of the source and collection ObservableSources and - * returns an item to be emitted by the resulting ObservableSource - * @return an Observable that emits the results of applying a function to a pair of values emitted by the - * source ObservableSource and the collection ObservableSource + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable} + * @param combiner + * a function that combines one item emitted by each of the source and collection {@code ObservableSource}s and + * returns an item to be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, - BiFunction resultSelector) { - return flatMap(mapper, resultSelector, false, bufferSize(), bufferSize()); + @NonNull + public final <@NonNull U, @NonNull R> Observable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner) { + return flatMap(mapper, combiner, false, bufferSize(), bufferSize()); } /** - * Returns an Observable that emits the results of a specified function to the pair of values emitted by the - * source ObservableSource and a specified collection ObservableSource. + * Returns an {@code Observable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Observable} and the mapped inner {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the collection ObservableSource + * the type of items emitted by the collection {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param mapper - * a function that returns an ObservableSource for each item emitted by the source ObservableSource + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable} * @param combiner - * a function that combines one item emitted by each of the source and collection ObservableSources and - * returns an item to be emitted by the resulting ObservableSource + * a function that combines one item emitted by each of the source and collection {@code ObservableSource}s and + * returns an item to be emitted by the resulting {@code Observable} * @param delayErrors - * if true, exceptions from the current Observable and all inner ObservableSources are delayed until all of them terminate - * if false, the first one signalling an exception will terminate the whole sequence immediately - * @return an Observable that emits the results of applying a function to a pair of values emitted by the - * source ObservableSource and the collection ObservableSource + * if {@code true}, exceptions from the current {@code Observable} and all inner {@code ObservableSource}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, - BiFunction combiner, boolean delayErrors) { + @NonNull + public final <@NonNull U, @NonNull R> Observable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, boolean delayErrors) { return flatMap(mapper, combiner, delayErrors, bufferSize(), bufferSize()); } /** - * Returns an Observable that emits the results of a specified function to the pair of values emitted by the - * source ObservableSource and a specified collection ObservableSource, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Returns an {@code Observable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Observable} and the mapped inner {@link ObservableSource}, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the collection ObservableSource + * the type of items emitted by the collection {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param mapper - * a function that returns an ObservableSource for each item emitted by the source ObservableSource + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable} * @param combiner - * a function that combines one item emitted by each of the source and collection ObservableSources and - * returns an item to be emitted by the resulting ObservableSource + * a function that combines one item emitted by each of the source and collection {@code ObservableSource}s and + * returns an item to be emitted by the resulting {@code Observable} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Observable and all inner ObservableSources are delayed until all of them terminate - * if false, the first one signalling an exception will terminate the whole sequence immediately - * @return an Observable that emits the results of applying a function to a pair of values emitted by the - * source ObservableSource and the collection ObservableSource + * if {@code true}, exceptions from the current {@code Observable} and all inner {@code ObservableSource}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, - BiFunction combiner, boolean delayErrors, int maxConcurrency) { + @NonNull + public final <@NonNull U, @NonNull R> Observable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, boolean delayErrors, int maxConcurrency) { return flatMap(mapper, combiner, delayErrors, maxConcurrency, bufferSize()); } /** - * Returns an Observable that emits the results of a specified function to the pair of values emitted by the - * source ObservableSource and a specified collection ObservableSource, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Returns an {@code Observable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Observable} and the mapped inner {@link ObservableSource}, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the collection ObservableSource + * the type of items emitted by the collection {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param mapper - * a function that returns an ObservableSource for each item emitted by the source ObservableSource + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable} * @param combiner - * a function that combines one item emitted by each of the source and collection ObservableSources and - * returns an item to be emitted by the resulting ObservableSource + * a function that combines one item emitted by each of the source and collection {@code ObservableSource}s and + * returns an item to be emitted by the resulting {@code Observable} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently * @param delayErrors - * if true, exceptions from the current Observable and all inner ObservableSources are delayed until all of them terminate - * if false, the first one signalling an exception will terminate the whole sequence immediately + * if {@code true}, exceptions from the current {@code Observable} and all inner {@code ObservableSource}s are delayed until all of them terminate + * if {@code false}, the first one signaling an exception will terminate the whole sequence immediately * @param bufferSize - * the number of elements to prefetch from the inner ObservableSources. - * @return an Observable that emits the results of applying a function to a pair of values emitted by the - * source ObservableSource and the collection ObservableSource + * the number of elements expected from the inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(final Function> mapper, - final BiFunction combiner, boolean delayErrors, int maxConcurrency, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + @NonNull + public final <@NonNull U, @NonNull R> Observable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, boolean delayErrors, int maxConcurrency, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); return flatMap(ObservableInternalHelper.flatMapWithCombiner(mapper, combiner), delayErrors, maxConcurrency, bufferSize); } /** - * Returns an Observable that emits the results of a specified function to the pair of values emitted by the - * source ObservableSource and a specified collection ObservableSource, while limiting the maximum number of concurrent - * subscriptions to these ObservableSources. + * Returns an {@code Observable} that emits the results of a specified function to the pair of values emitted by the + * current {@code Observable} and the mapped inner {@link ObservableSource}, while limiting the maximum number of concurrent + * subscriptions to these {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the collection ObservableSource + * the type of items emitted by the collection {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param mapper - * a function that returns an ObservableSource for each item emitted by the source ObservableSource + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code Observable} * @param combiner - * a function that combines one item emitted by each of the source and collection ObservableSources and - * returns an item to be emitted by the resulting ObservableSource + * a function that combines one item emitted by each of the source and collection {@code ObservableSource}s and + * returns an item to be emitted by the resulting {@code Observable} * @param maxConcurrency - * the maximum number of ObservableSources that may be subscribed to concurrently - * @return an Observable that emits the results of applying a function to a pair of values emitted by the - * source ObservableSource and the collection ObservableSource + * the maximum number of {@code ObservableSource}s that may be subscribed to concurrently + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMap(Function> mapper, - BiFunction combiner, int maxConcurrency) { + @NonNull + public final <@NonNull U, @NonNull R> Observable flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner, int maxConcurrency) { return flatMap(mapper, combiner, false, maxConcurrency, bufferSize()); } /** - * Maps each element of the upstream Observable into CompletableSources, subscribes to them and - * waits until the upstream and all CompletableSources complete. + * Maps each element of the current {@code Observable} into {@link CompletableSource}s, subscribes to them and + * waits until the upstream and all {@code CompletableSource}s complete. *

* *

*
Scheduler:
*
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
- * @param mapper the function that received each source value and transforms them into CompletableSources. - * @return the new Completable instance + * @param mapper the function that received each source value and transforms them into {@code CompletableSource}s. + * @throws NullPointerException if {@code mapper} is {@code null} + * @return the new {@link Completable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable flatMapCompletable(Function mapper) { + @NonNull + public final Completable flatMapCompletable(@NonNull Function mapper) { return flatMapCompletable(mapper, false); } /** - * Maps each element of the upstream Observable into CompletableSources, subscribes to them and - * waits until the upstream and all CompletableSources complete, optionally delaying all errors. + * Maps each element of the current {@code Observable} into {@link CompletableSource}s, subscribes to them and + * waits until the upstream and all {@code CompletableSource}s complete, optionally delaying all errors. *

- * + * *

*
Scheduler:
*
{@code flatMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
- * @param mapper the function that received each source value and transforms them into CompletableSources. - * @param delayErrors if true errors from the upstream and inner CompletableSources are delayed until each of them - * terminates. - * @return the new Completable instance + * @param mapper the function that received each source value and transforms them into {@code CompletableSource}s. + * @param delayErrors if {@code true}, errors from the upstream and inner {@code CompletableSource}s are delayed until all of them + * terminate. + * @return the new {@link Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Completable flatMapCompletable(Function mapper, boolean delayErrors) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableFlatMapCompletableCompletable(this, mapper, delayErrors)); + @NonNull + public final Completable flatMapCompletable(@NonNull Function mapper, boolean delayErrors) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableFlatMapCompletableCompletable<>(this, mapper, delayErrors)); } /** - * Returns an Observable that merges each item emitted by the source ObservableSource with the values in an - * Iterable corresponding to that item that is generated by a selector. + * Merges {@link Iterable}s generated by a mapper {@link Function} for each individual item emitted by + * the current {@code Observable} into a single {@code Observable} sequence. *

* *

@@ -8808,24 +9526,27 @@ public final Completable flatMapCompletable(Function * * @param - * the type of item emitted by the resulting Iterable + * the output type and the element type of the {@code Iterable}s * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source ObservableSource - * @return an Observable that emits the results of merging the items emitted by the source ObservableSource with - * the values in the Iterables corresponding to those items, as generated by {@code collectionSelector} + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapIterable(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableFlattenIterable(this, mapper)); + @NonNull + public final <@NonNull U> Observable flatMapIterable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableFlattenIterable<>(this, mapper)); } /** - * Returns an Observable that emits the results of applying a function to the pair of values from the source - * ObservableSource and an Iterable corresponding to that item that is generated by a selector. + * Merges {@link Iterable}s generated by a mapper {@link Function} for each individual item emitted by + * the current {@code Observable} into a single {@code Observable} sequence where the resulting items will + * be the combination of the original item and each inner item of the respective {@code Iterable} as returned + * by the {@code resultSelector} {@link BiFunction}. *

* *

@@ -8834,114 +9555,124 @@ public final Observable flatMapIterable(final Function * * @param - * the collection element type + * the element type of the {@code Iterable}s * @param - * the type of item emitted by the resulting Iterable + * the output type as determined by the {@code resultSelector} function * @param mapper - * a function that returns an Iterable sequence of values for each item emitted by the source - * ObservableSource - * @param resultSelector - * a function that returns an item based on the item emitted by the source ObservableSource and the - * Iterable returned for that item by the {@code collectionSelector} - * @return an Observable that emits the items returned by {@code resultSelector} for each item in the source - * ObservableSource + * a function that returns an {@code Iterable} sequence of values for each item emitted by the current + * {@code Observable} + * @param combiner + * a function that returns an item based on the item emitted by the current {@code Observable} and the + * next item of the {@code Iterable} returned for that original item by the {@code mapper} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapIterable(final Function> mapper, - BiFunction resultSelector) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); - return flatMap(ObservableInternalHelper.flatMapIntoIterable(mapper), resultSelector, false, bufferSize(), bufferSize()); + @NonNull + public final <@NonNull U, @NonNull V> Observable flatMapIterable(@NonNull Function> mapper, + @NonNull BiFunction combiner) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return flatMap(ObservableInternalHelper.flatMapIntoIterable(mapper), combiner, false, bufferSize(), bufferSize()); } /** - * Maps each element of the upstream Observable into MaybeSources, subscribes to all of them - * and merges their onSuccess values, in no particular order, into a single Observable sequence. + * Maps each element of the current {@code Observable} into {@link MaybeSource}s, subscribes to all of them + * and merges their {@code onSuccess} values, in no particular order, into a single {@code Observable} sequence. *

- * + * *

*
Scheduler:
*
{@code flatMapMaybe} does not operate by default on a particular {@link Scheduler}.
*
* @param the result value type - * @param mapper the function that received each source value and transforms them into MaybeSources. - * @return the new Observable instance + * @param mapper the function that received each source value and transforms them into {@code MaybeSource}s. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapMaybe(Function> mapper) { + @NonNull + public final <@NonNull R> Observable flatMapMaybe(@NonNull Function> mapper) { return flatMapMaybe(mapper, false); } /** - * Maps each element of the upstream Observable into MaybeSources, subscribes to them - * and merges their onSuccess values, in no particular order, into a single Observable sequence, + * Maps each element of the current {@code Observable} into {@link MaybeSource}s, subscribes to them + * and merges their {@code onSuccess} values, in no particular order, into a single {@code Observable} sequence, * optionally delaying all errors. *

- * + * *

*
Scheduler:
*
{@code flatMapMaybe} does not operate by default on a particular {@link Scheduler}.
*
* @param the result value type - * @param mapper the function that received each source value and transforms them into MaybeSources. - * @param delayErrors if true errors from the upstream and inner MaybeSources are delayed until each of them - * terminates. - * @return the new Observable instance + * @param mapper the function that received each source value and transforms them into {@code MaybeSource}s. + * @param delayErrors if {@code true}, errors from the upstream and inner {@code MaybeSource}s are delayed until all of them + * terminate. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapMaybe(Function> mapper, boolean delayErrors) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableFlatMapMaybe(this, mapper, delayErrors)); + @NonNull + public final <@NonNull R> Observable flatMapMaybe(@NonNull Function> mapper, boolean delayErrors) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableFlatMapMaybe<>(this, mapper, delayErrors)); } /** - * Maps each element of the upstream Observable into SingleSources, subscribes to all of them - * and merges their onSuccess values, in no particular order, into a single Observable sequence. + * Maps each element of the current {@code Observable} into {@link SingleSource}s, subscribes to all of them + * and merges their {@code onSuccess} values, in no particular order, into a single {@code Observable} sequence. *

- * + * *

*
Scheduler:
*
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
*
* @param the result value type - * @param mapper the function that received each source value and transforms them into SingleSources. - * @return the new Observable instance + * @param mapper the function that received each source value and transforms them into {@code SingleSource}s. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapSingle(Function> mapper) { + @NonNull + public final <@NonNull R> Observable flatMapSingle(@NonNull Function> mapper) { return flatMapSingle(mapper, false); } /** - * Maps each element of the upstream Observable into SingleSources, subscribes to them - * and merges their onSuccess values, in no particular order, into a single Observable sequence, + * Maps each element of the current {@code Observable} into {@link SingleSource}s, subscribes to them + * and merges their {@code onSuccess} values, in no particular order, into a single {@code Observable} sequence, * optionally delaying all errors. *

- * + * *

*
Scheduler:
*
{@code flatMapSingle} does not operate by default on a particular {@link Scheduler}.
*
* @param the result value type - * @param mapper the function that received each source value and transforms them into SingleSources. - * @param delayErrors if true errors from the upstream and inner SingleSources are delayed until each of them + * @param mapper the function that received each source value and transforms them into {@code SingleSource}s. + * @param delayErrors if {@code true}, errors from the upstream and inner {@code SingleSource}s are delayed until each of them * terminates. - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapSingle(Function> mapper, boolean delayErrors) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableFlatMapSingle(this, mapper, delayErrors)); + @NonNull + public final <@NonNull R> Observable flatMapSingle(@NonNull Function> mapper, boolean delayErrors) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableFlatMapSingle<>(this, mapper, delayErrors)); } /** - * Subscribes to the {@link ObservableSource} and receives notifications for each element. + * Subscribes to the {@link ObservableSource} and calls a {@link Consumer} for each item of the current {@code Observable} + * on its emission thread. *

* *

@@ -8952,125 +9683,129 @@ public final Observable flatMapSingle(Function * * @param onNext - * {@link Consumer} to execute for each item. + * the {@code Consumer} to execute for each item. * @return - * a Disposable that allows disposing of an asynchronous sequence + * a {@link Disposable} that allows disposing the sequence if the current {@code Observable} runs asynchronously * @throws NullPointerException - * if {@code onNext} is null + * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEach(Consumer onNext) { + @NonNull + public final Disposable forEach(@NonNull Consumer onNext) { return subscribe(onNext); } /** - * Subscribes to the {@link ObservableSource} and receives notifications for each element until the - * onNext Predicate returns false. + * Subscribes to the {@link ObservableSource} and calls a {@link Predicate} for each item of the current {@code Observable}, + * on its emission thread, until the predicate returns {@code false}. *

- * + * *

- * If the Observable emits an error, it is wrapped into an - * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * If the {@code Observable} emits an error, it is wrapped into an + * {@link OnErrorNotImplementedException} + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code forEachWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * {@link Predicate} to execute for each item. + * the {@code Predicate} to execute for each item. * @return - * a Disposable that allows disposing of an asynchronous sequence + * a {@link Disposable} that allows disposing the sequence if the current {@code Observable} runs asynchronously * @throws NullPointerException - * if {@code onNext} is null + * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEachWhile(Predicate onNext) { + @NonNull + public final Disposable forEachWhile(@NonNull Predicate onNext) { return forEachWhile(onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to the {@link ObservableSource} and receives notifications for each element and error events until the - * onNext Predicate returns false. + * Subscribes to the {@link ObservableSource} and calls a {@link Predicate} for each item or a {@link Consumer} with the error + * of the current {@code Observable}, on their original emission threads, until the predicate returns {@code false}. *
*
Scheduler:
*
{@code forEachWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * {@link Predicate} to execute for each item. + * the {@code Predicate} to execute for each item. * @param onError - * {@link Consumer} to execute when an error is emitted. + * the {@code Consumer} to execute when an error is emitted. * @return - * a Disposable that allows disposing of an asynchronous sequence + * a {@link Disposable} that allows disposing the sequence if the current {@code Observable} runs asynchronously * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null + * if {@code onNext} or {@code onError} is {@code null} * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEachWhile(Predicate onNext, Consumer onError) { + @NonNull + public final Disposable forEachWhile(@NonNull Predicate onNext, @NonNull Consumer onError) { return forEachWhile(onNext, onError, Functions.EMPTY_ACTION); } /** - * Subscribes to the {@link ObservableSource} and receives notifications for each element and the terminal events until the - * onNext Predicate returns false. + * Subscribes to the {@link ObservableSource} and calls a {@link Predicate} for each item, a {@link Consumer} with the error + * or an {@link Action} upon completion of the current {@code Observable}, on their original emission threads, + * until the predicate returns {@code false}. *
*
Scheduler:
*
{@code forEachWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * {@link Predicate} to execute for each item. + * the {@code Predicate} to execute for each item. * @param onError - * {@link Consumer} to execute when an error is emitted. + * the {@code Consumer} to execute when an error is emitted. * @param onComplete - * {@link Action} to execute when completion is signalled. + * the {@code Action} to execute when completion is signaled. * @return - * a Disposable that allows disposing of an asynchronous sequence + * a {@link Disposable} that allows disposing the sequence if the current {@code Observable} runs asynchronously * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null, or - * if {@code onComplete} is null + * if {@code onNext} or {@code onError} or {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable forEachWhile(final Predicate onNext, Consumer onError, - final Action onComplete) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + @NonNull + public final Disposable forEachWhile(@NonNull Predicate onNext, @NonNull Consumer onError, + @NonNull Action onComplete) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); - ForEachWhileObserver o = new ForEachWhileObserver(onNext, onError, onComplete); + ForEachWhileObserver o = new ForEachWhileObserver<>(onNext, onError, onComplete); subscribe(o); return o; } /** - * Groups the items emitted by an {@code ObservableSource} according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s. The emitted {@code GroupedObservableSource} allows only a single - * {@link Observer} during its lifetime and if this {@code Observer} calls dispose() before the - * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedObservableSource} emission. + * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these + * grouped items as {@link GroupedObservable}s. + *

+ * *

- * + * Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its + * lifetime and if this {@code Observer} calls {@code dispose()} before the + * source terminates, the next emission by the source having the same key will trigger a new + * {@code GroupedObservable} emission. *

- * Note: A {@link GroupedObservable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedObservable} 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 - * {@code GroupedObservableSource}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedObservable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Scheduler:
@@ -9081,36 +9816,38 @@ public final Disposable forEachWhile(final Predicate onNext, Consumer * a function that extracts the key for each item * @param * the key type - * @return an {@code ObservableSource} that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source ObservableSource that share that - * key value + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: GroupBy */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> groupBy(Function keySelector) { + @NonNull + public final <@NonNull K> Observable> groupBy(@NonNull Function keySelector) { return groupBy(keySelector, (Function)Functions.identity(), false, bufferSize()); } /** - * Groups the items emitted by an {@code ObservableSource} according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s. The emitted {@code GroupedObservableSource} allows only a single - * {@link Observer} during its lifetime and if this {@code Observer} calls dispose() before the - * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedObservableSource} emission. + * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these + * grouped items as {@link GroupedObservable}s. + *

+ * *

- * + * Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its + * lifetime and if this {@code Observer} calls {@code dispose()} before the + * source terminates, the next emission by the source having the same key will trigger a new + * {@code GroupedObservable} emission. *

- * Note: A {@link GroupedObservable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedObservable} 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 - * {@code GroupedObservableSource}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedObservable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Scheduler:
@@ -9122,38 +9859,40 @@ public final Observable> groupBy(Function * the key type * @param delayError - * if true, the exception from the current Observable is delayed in each group until that specific group emitted - * the normal values; if false, the exception bypasses values in the groups and is reported immediately. - * @return an {@code ObservableSource} that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source ObservableSource that share that - * key value + * if {@code true}, the exception from the current {@code Observable} is delayed in each group until that specific group emitted + * the normal values; if {@code false}, the exception bypasses values in the groups and is reported immediately. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: GroupBy */ @SuppressWarnings({ "unchecked", "rawtypes" }) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> groupBy(Function keySelector, boolean delayError) { + @NonNull + public final <@NonNull K> Observable> groupBy(@NonNull Function keySelector, boolean delayError) { return groupBy(keySelector, (Function)Functions.identity(), delayError, bufferSize()); } /** - * Groups the items emitted by an {@code ObservableSource} according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s. The emitted {@code GroupedObservableSource} allows only a single - * {@link Observer} during its lifetime and if this {@code Observer} calls dispose() before the - * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedObservableSource} emission. + * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these + * grouped items as {@link GroupedObservable}s. + *

+ * *

- * + * Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its + * lifetime and if this {@code Observer} calls {@code dispose()} before the + * source terminates, the next emission by the source having the same key will trigger a new + * {@code GroupedObservable} emission. *

- * Note: A {@link GroupedObservable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedObservable} 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 - * {@code GroupedObservableSource}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedObservable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Scheduler:
@@ -9168,36 +9907,38 @@ public final Observable> groupBy(Function * the element type - * @return an {@code ObservableSource} that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source ObservableSource that share that - * key value + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: GroupBy */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> groupBy(Function keySelector, + @NonNull + public final <@NonNull K, @NonNull V> Observable> groupBy(@NonNull Function keySelector, Function valueSelector) { return groupBy(keySelector, valueSelector, false, bufferSize()); } /** - * Groups the items emitted by an {@code ObservableSource} according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s. The emitted {@code GroupedObservableSource} allows only a single - * {@link Observer} during its lifetime and if this {@code Observer} calls dispose() before the - * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedObservableSource} emission. + * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these + * grouped items as {@link GroupedObservable}s. + *

+ * *

- * + * Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its + * lifetime and if this {@code Observer} calls {@code dispose()} before the + * source terminates, the next emission by the source having the same key will trigger a new + * {@code GroupedObservable} emission. *

- * Note: A {@link GroupedObservable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedObservable} 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 - * {@code GroupedObservableSource}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedObservable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Scheduler:
@@ -9213,38 +9954,40 @@ public final Observable> groupBy(Function * the element type * @param delayError - * if true, the exception from the current Observable is delayed in each group until that specific group emitted - * the normal values; if false, the exception bypasses values in the groups and is reported immediately. - * @return an {@code ObservableSource} that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source ObservableSource that share that - * key value + * if {@code true}, the exception from the current {@code Observable} is delayed in each group until that specific group emitted + * the normal values; if {@code false}, the exception bypasses values in the groups and is reported immediately. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: GroupBy */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> groupBy(Function keySelector, - Function valueSelector, boolean delayError) { + @NonNull + public final <@NonNull K, @NonNull V> Observable> groupBy(@NonNull Function keySelector, + @NonNull Function valueSelector, boolean delayError) { return groupBy(keySelector, valueSelector, delayError, bufferSize()); } /** - * Groups the items emitted by an {@code ObservableSource} according to a specified criterion, and emits these - * grouped items as {@link GroupedObservable}s. The emitted {@code GroupedObservableSource} allows only a single - * {@link Observer} during its lifetime and if this {@code Observer} calls dispose() before the - * source terminates, the next emission by the source having the same key will trigger a new - * {@code GroupedObservableSource} emission. + * Groups the items emitted by the current {@code Observable} according to a specified criterion, and emits these + * grouped items as {@link GroupedObservable}s. + *

+ * *

- * + * Each emitted {@code GroupedObservable} allows only a single {@link Observer} to subscribe to it during its + * lifetime and if this {@code Observer} calls {@code dispose()} before the + * source terminates, the next emission by the source having the same key will trigger a new + * {@code GroupedObservable} emission. *

- * Note: A {@link GroupedObservable} will cache the items it is to emit until such time as it + * Note: A {@code GroupedObservable} 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 - * {@code GroupedObservableSource}s that do not concern you. Instead, you can signal to them that they may + * {@code GroupedObservable}s that do not concern you. Instead, you can signal to them that they may * discard their buffers by applying an operator like {@link #ignoreElements} to them. *

* Note also that ignoring groups or subscribing later (i.e., on another thread) will result in * so-called group abandonment where a group will only contain one element and the group will be * re-created over and over as new upstream items trigger a new group. The behavior is - * a tradeoff between no-dataloss, upstream cancellation and excessive group creation. + * a trade-off between no-dataloss, upstream cancellation and excessive group creation. * *

*
Scheduler:
@@ -9256,81 +9999,84 @@ public final Observable> groupBy(Function * the key type * @param * the element type - * @return an {@code ObservableSource} that emits {@link GroupedObservable}s, each of which corresponds to a - * unique key value and each of which emits those items from the source ObservableSource that share that - * key value + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: GroupBy */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> groupBy(Function keySelector, - Function valueSelector, + @NonNull + public final <@NonNull K, @NonNull V> Observable> groupBy(@NonNull Function keySelector, + @NonNull Function valueSelector, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableGroupBy(this, keySelector, valueSelector, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new ObservableGroupBy<>(this, keySelector, valueSelector, bufferSize, delayError)); } /** - * Returns an Observable that correlates two ObservableSources when they overlap in time and groups the results. + * Returns an {@code Observable} that correlates two {@link ObservableSource}s when they overlap in time and groups the results. *

* There are no guarantees in what order the items get combined when multiple - * items from one or both source ObservableSources overlap. + * items from one or both source {@code ObservableSource}s overlap. *

- * + * *

*
Scheduler:
*
{@code groupJoin} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the right ObservableSource source - * @param the element type of the left duration ObservableSources - * @param the element type of the right duration ObservableSources + * @param the value type of the right {@code ObservableSource} source + * @param the element type of the left duration {@code ObservableSource}s + * @param the element type of the right duration {@code ObservableSource}s * @param the result type * @param other - * the other ObservableSource to correlate items from the source ObservableSource with + * the other {@code ObservableSource} to correlate items from the current {@code Observable} with * @param leftEnd - * a function that returns an ObservableSource whose emissions indicate the duration of the values of - * the source ObservableSource + * a function that returns an {@code ObservableSource} whose emissions indicate the duration of the values of + * the current {@code Observable} * @param rightEnd - * a function that returns an ObservableSource whose emissions indicate the duration of the values of - * the {@code right} ObservableSource + * a function that returns an {@code ObservableSource} whose emissions indicate the duration of the values of + * the {@code right} {@code ObservableSource} * @param resultSelector - * a function that takes an item emitted by each ObservableSource and returns the value to be emitted - * by the resulting ObservableSource - * @return an Observable that emits items based on combining those items emitted by the source ObservableSources - * whose durations overlap + * a function that takes an item emitted by each {@code ObservableSource} and returns the value to be emitted + * by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other}, {@code leftEnd}, {@code rightEnd} or {@code resultSelector} is {@code null} * @see ReactiveX operators documentation: Join */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable groupJoin( - ObservableSource other, - Function> leftEnd, - Function> rightEnd, - BiFunction, ? extends R> resultSelector - ) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(leftEnd, "leftEnd is null"); - ObjectHelper.requireNonNull(rightEnd, "rightEnd is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); - return RxJavaPlugins.onAssembly(new ObservableGroupJoin( + @NonNull + public final <@NonNull TRight, @NonNull TLeftEnd, @NonNull TRightEnd, @NonNull R> Observable groupJoin( + @NonNull ObservableSource other, + @NonNull Function> leftEnd, + @NonNull Function> rightEnd, + @NonNull BiFunction, ? extends R> resultSelector + ) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(leftEnd, "leftEnd is null"); + Objects.requireNonNull(rightEnd, "rightEnd is null"); + Objects.requireNonNull(resultSelector, "resultSelector is null"); + return RxJavaPlugins.onAssembly(new ObservableGroupJoin<>( this, other, leftEnd, rightEnd, resultSelector)); } /** - * Hides the identity of this Observable and its Disposable. - *

Allows hiding extra features such as {@link io.reactivex.rxjava3.subjects.Subject}'s + * Hides the identity of the current {@code Observable} and its {@link Disposable}. + *

+ * Allows hiding extra features such as {@link io.reactivex.rxjava3.subjects.Subject}'s * {@link Observer} methods or preventing certain identity-based * optimizations (fusion). *

@@ -9339,148 +10085,153 @@ public final Observable groupJoin( *

Scheduler:
*
{@code hide} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Observable instance + * @return the new {@code Observable} instance * * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable hide() { - return RxJavaPlugins.onAssembly(new ObservableHide(this)); + return RxJavaPlugins.onAssembly(new ObservableHide<>(this)); } /** - * Ignores all items emitted by the source ObservableSource and only calls {@code onComplete} or {@code onError}. + * Ignores all items emitted by the current {@code Observable} and only calls {@code onComplete} or {@code onError}. *

- * + * *

*
Scheduler:
*
{@code ignoreElements} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Completable instance + * @return the new {@link Completable} instance * @see ReactiveX operators documentation: IgnoreElements */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable ignoreElements() { - return RxJavaPlugins.onAssembly(new ObservableIgnoreElementsCompletable(this)); + return RxJavaPlugins.onAssembly(new ObservableIgnoreElementsCompletable<>(this)); } /** - * Returns a Single that emits {@code true} if the source ObservableSource is empty, otherwise {@code false}. + * Returns a {@link Single} that emits {@code true} if the current {@code Observable} is empty, otherwise {@code false}. *

- * In Rx.Net this is negated as the {@code any} Observer but we renamed this in RxJava to better match Java + * In Rx.Net this is negated as the {@code any} {@link Observer} but we renamed this in RxJava to better match Java * naming idioms. *

- * + * *

*
Scheduler:
*
{@code isEmpty} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a Boolean + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Contains */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single isEmpty() { return all(Functions.alwaysFalse()); } /** - * Correlates the items emitted by two ObservableSources based on overlapping durations. + * Correlates the items emitted by two {@link ObservableSource}s based on overlapping durations. *

* There are no guarantees in what order the items get combined when multiple - * items from one or both source ObservableSources overlap. + * items from one or both source {@code ObservableSource}s overlap. *

- * + * *

*
Scheduler:
*
{@code join} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the value type of the right ObservableSource source - * @param the element type of the left duration ObservableSources - * @param the element type of the right duration ObservableSources + * @param the value type of the right {@code ObservableSource} source + * @param the element type of the left duration {@code ObservableSource}s + * @param the element type of the right duration {@code ObservableSource}s * @param the result type * @param other - * the second ObservableSource to join items from + * the second {@code ObservableSource} to join items from * @param leftEnd - * a function to select a duration for each item emitted by the source ObservableSource, used to + * a function to select a duration for each item emitted by the current {@code Observable}, used to * determine overlap * @param rightEnd - * a function to select a duration for each item emitted by the {@code right} ObservableSource, used to + * a function to select a duration for each item emitted by the {@code right} {@code ObservableSource}, used to * determine overlap * @param resultSelector - * a function that computes an item to be emitted by the resulting ObservableSource for any two - * overlapping items emitted by the two ObservableSources - * @return an Observable that emits items correlating to items emitted by the source ObservableSources that have - * overlapping durations + * a function that computes an item to be emitted by the resulting {@code Observable} for any two + * overlapping items emitted by the two {@code ObservableSource}s + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other}, {@code leftEnd}, {@code rightEnd} or {@code resultSelector} is {@code null} * @see ReactiveX operators documentation: Join */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable join( - ObservableSource other, - Function> leftEnd, - Function> rightEnd, - BiFunction resultSelector - ) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(leftEnd, "leftEnd is null"); - ObjectHelper.requireNonNull(rightEnd, "rightEnd is null"); - ObjectHelper.requireNonNull(resultSelector, "resultSelector is null"); + @NonNull + public final <@NonNull TRight, @NonNull TLeftEnd, @NonNull TRightEnd, @NonNull R> Observable join( + @NonNull ObservableSource other, + @NonNull Function> leftEnd, + @NonNull Function> rightEnd, + @NonNull BiFunction resultSelector + ) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(leftEnd, "leftEnd is null"); + Objects.requireNonNull(rightEnd, "rightEnd is null"); + Objects.requireNonNull(resultSelector, "resultSelector is null"); return RxJavaPlugins.onAssembly(new ObservableJoin( this, other, leftEnd, rightEnd, resultSelector)); } /** - * Returns a Maybe that emits the last item emitted by this Observable or - * completes if this Observable is empty. + * Returns a {@link Maybe} that emits the last item emitted by the current {@code Observable} or + * completes if the current {@code Observable} is empty. *

- * + * *

*
Scheduler:
*
{@code lastElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Maybe that emits the last item from the source ObservableSource or notifies observers of an - * error + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: Last */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe lastElement() { - return RxJavaPlugins.onAssembly(new ObservableLastMaybe(this)); + return RxJavaPlugins.onAssembly(new ObservableLastMaybe<>(this)); } /** - * Returns a Single that emits only the last item emitted by this Observable, or a default item - * if this Observable completes without emitting any items. + * Returns a {@link Single} that emits only the last item emitted by the current {@code Observable}, or a default item + * if the current {@code Observable} completes without emitting any items. *

- * + * *

*
Scheduler:
*
{@code last} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * the default item to emit if the source ObservableSource is empty - * @return a Single that emits only the last item emitted by the source ObservableSource, or a default item - * if the source ObservableSource is empty + * the default item to emit if the current {@code Observable} is empty + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: Last */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single last(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); - return RxJavaPlugins.onAssembly(new ObservableLastSingle(this, defaultItem)); + @NonNull + public final Single last(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new ObservableLastSingle<>(this, defaultItem)); } /** - * Returns a Single that emits only the last item emitted by this Observable or - * signals a {@link NoSuchElementException} if this Observable is empty. + * Returns a {@link Single} that emits only the last item emitted by the current {@code Observable} or + * signals a {@link NoSuchElementException} if the current {@code Observable} is empty. *

* *

@@ -9488,14 +10239,14 @@ public final Single last(T defaultItem) { *
{@code lastOrError} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits only the last item emitted by the source ObservableSource. - * If the source ObservableSource completes without emitting any items a {@link NoSuchElementException} will be thrown. + * @return the new {@code Single} instance * @see ReactiveX operators documentation: Last */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single lastOrError() { - return RxJavaPlugins.onAssembly(new ObservableLastSingle(this, null)); + return RxJavaPlugins.onAssembly(new ObservableLastSingle<>(this, null)); } /** @@ -9503,7 +10254,7 @@ public final Single lastOrError() { * other standard composition methods first; * Returns an {@code Observable} which, when subscribed to, invokes the {@link ObservableOperator#apply(Observer) apply(Observer)} method * of the provided {@link ObservableOperator} for each individual downstream {@link Observer} and allows the - * insertion of a custom operator by accessing the downstream's {@link Observer} during this subscription phase + * insertion of a custom operator by accessing the downstream's {@code Observer} during this subscription phase * and providing a new {@code Observer}, containing the custom operator's intended business logic, that will be * used in the subscription process going further upstream. *

@@ -9620,38 +10371,40 @@ public final Single lastOrError() { * class and creating an {@link ObservableTransformer} with it is recommended. *

* Note also that it is not possible to stop the subscription phase in {@code lift()} as the {@code apply()} method - * requires a non-null {@code Observer} instance to be returned, which is then unconditionally subscribed to - * the upstream {@code Observable}. For example, if the operator decided there is no reason to subscribe to the + * requires a non-{@code null} {@code Observer} instance to be returned, which is then unconditionally subscribed to + * the current {@code Observable}. For example, if the operator decided there is no reason to subscribe to the * upstream source because of some optimization possibility or a failure to prepare the operator, it still has to - * return an {@code Observer} that should immediately dispose the upstream's {@code Disposable} in its + * return an {@code Observer} that should immediately dispose the upstream's {@link Disposable} in its * {@code onSubscribe} method. Again, using an {@code ObservableTransformer} and extending the {@code Observable} is * a better option as {@link #subscribeActual} can decide to not subscribe to its upstream after all. *

*
Scheduler:
*
{@code lift} does not operate by default on a particular {@link Scheduler}, however, the - * {@link ObservableOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
+ * {@code ObservableOperator} may use a {@code Scheduler} to support its own asynchronous behavior. *
* * @param the output value type - * @param lifter the {@link ObservableOperator} that receives the downstream's {@code Observer} and should return + * @param lifter the {@code ObservableOperator} that receives the downstream's {@code Observer} and should return * an {@code Observer} with custom behavior to be used as the consumer for the current * {@code Observable}. - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code lifter} is {@code null} * @see RxJava wiki: Writing operators * @see #compose(ObservableTransformer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable lift(ObservableOperator lifter) { - ObjectHelper.requireNonNull(lifter, "lifter is null"); - return RxJavaPlugins.onAssembly(new ObservableLift(this, lifter)); + @NonNull + public final <@NonNull R> Observable lift(@NonNull ObservableOperator lifter) { + Objects.requireNonNull(lifter, "lifter is null"); + return RxJavaPlugins.onAssembly(new ObservableLift<>(this, lifter)); } /** - * Returns an Observable that applies a specified function to each item emitted by the source ObservableSource and + * Returns an {@code Observable} that applies a specified function to each item emitted by the current {@code Observable} and * emits the results of these function applications. *

- * + * *

*
Scheduler:
*
{@code map} does not operate by default on a particular {@link Scheduler}.
@@ -9659,45 +10412,46 @@ public final Observable lift(ObservableOperator l * * @param the output type * @param mapper - * a function to apply to each item emitted by the ObservableSource - * @return an Observable that emits the items from the source ObservableSource, transformed by the specified - * function + * a function to apply to each item emitted by the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: Map */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable map(Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableMap(this, mapper)); + @NonNull + public final <@NonNull R> Observable map(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableMap<>(this, mapper)); } /** - * Returns an Observable that represents all of the emissions and notifications from the source - * ObservableSource into emissions marked with their original types within {@link Notification} objects. + * Returns an {@code Observable} that represents all of the emissions and notifications from the current + * {@code Observable} into emissions marked with their original types within {@link Notification} objects. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an Observable that emits items that are the result of materializing the items and notifications - * of the source ObservableSource + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Materialize * @see #dematerialize(Function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable> materialize() { - return RxJavaPlugins.onAssembly(new ObservableMaterialize(this)); + return RxJavaPlugins.onAssembly(new ObservableMaterialize<>(this)); } /** - * Flattens this and another ObservableSource into a single ObservableSource, without any transformation. + * Flattens the current {@code Observable} and another {@link ObservableSource} into a single {@code Observable} sequence, without any transformation. *

- * + * *

- * You can combine items emitted by multiple ObservableSources so that they appear as a single ObservableSource, by + * You can combine items emitted by multiple {@code ObservableSource}s so that they appear as a single {@code ObservableSource}, by * using the {@code mergeWith} method. *

*
Scheduler:
@@ -9705,23 +10459,25 @@ public final Observable> materialize() { *
* * @param other - * an ObservableSource to be merged - * @return an Observable that emits all of the items emitted by the source ObservableSources + * an {@code ObservableSource} to be merged + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Merge */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable mergeWith(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + @NonNull + public final Observable mergeWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); return merge(this, other); } /** - * Merges the sequence of items of this Observable with the success value of the other SingleSource. + * Merges the sequence of items of the current {@code Observable} with the success value of the other {@link SingleSource}. *

- * + * *

- * The success value of the other {@code SingleSource} can get interleaved at any point of this + * The success value of the other {@code SingleSource} can get interleaved at any point of the current * {@code Observable} sequence. *

*
Scheduler:
@@ -9729,23 +10485,25 @@ public final Observable mergeWith(ObservableSource other) { *
*

History: 2.1.10 - experimental * @param other the {@code SingleSource} whose success value to merge with - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable mergeWith(@NonNull SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableMergeWithSingle(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableMergeWithSingle<>(this, other)); } /** - * Merges the sequence of items of this Observable with the success value of the other MaybeSource - * or waits both to complete normally if the MaybeSource is empty. + * Merges the sequence of items of the current {@code Observable} with the success value of the other {@link MaybeSource} + * or waits both to complete normally if the {@code MaybeSource} is empty. *

- * + * *

- * The success value of the other {@code MaybeSource} can get interleaved at any point of this + * The success value of the other {@code MaybeSource} can get interleaved at any point of the current * {@code Observable} sequence. *

*
Scheduler:
@@ -9753,61 +10511,65 @@ public final Observable mergeWith(@NonNull SingleSource other) { *
*

History: 2.1.10 - experimental * @param other the {@code MaybeSource} which provides a success value to merge with or completes - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable mergeWith(@NonNull MaybeSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableMergeWithMaybe(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableMergeWithMaybe<>(this, other)); } /** - * Relays the items of this Observable and completes only when the other CompletableSource completes + * Relays the items of the current {@code Observable} and completes only when the other {@link CompletableSource} completes * as well. *

- * + * *

*
Scheduler:
*
{@code mergeWith} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.10 - experimental * @param other the {@code CompletableSource} to await for completion - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable mergeWith(@NonNull CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableMergeWithCompletable(this, other)); + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableMergeWithCompletable<>(this, other)); } /** - * Modifies an ObservableSource to perform its emissions and notifications on a specified {@link Scheduler}, + * Returns an {@code Observable} to perform the current {@code Observable}'s emissions and notifications on a specified {@link Scheduler}, * asynchronously with an unbounded buffer with {@link Flowable#bufferSize()} "island size". * - *

Note that onError notifications will cut ahead of onNext notifications on the emission thread if Scheduler is truly + *

Note that {@code onError} notifications will cut ahead of {@code onNext} notifications on the emission thread if {@code Scheduler} is truly * asynchronous. If strict event ordering is required, consider using the {@link #observeOn(Scheduler, boolean)} overload. *

- * + * *

- * This operator keeps emitting as many signals as it can on the given Scheduler's Worker thread, + * This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s worker thread, * which may result in a longer than expected occupation of this thread. In other terms, * it does not allow per-signal fairness in case the worker runs on a shared underlying thread. * If such fairness and signal/work interleaving is preferred, use the delay operator with zero time instead. *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
*

"Island size" indicates how large chunks the unbounded buffer allocates to store the excess elements waiting to be consumed * on the other side of the asynchronous boundary. * * @param scheduler - * the {@link Scheduler} to notify {@link Observer}s on - * @return the source ObservableSource modified so that its {@link Observer}s are notified on the specified - * {@link Scheduler} + * the {@code Scheduler} to notify {@link Observer}s on + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -9817,35 +10579,36 @@ public final Observable mergeWith(@NonNull CompletableSource other) { */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable observeOn(Scheduler scheduler) { + @NonNull + public final Observable observeOn(@NonNull Scheduler scheduler) { return observeOn(scheduler, false, bufferSize()); } /** - * Modifies an ObservableSource to perform its emissions and notifications on a specified {@link Scheduler}, - * asynchronously with an unbounded buffer with {@link Flowable#bufferSize()} "island size" and optionally delays onError notifications. + * Returns an {@code Observable} to perform the current {@code Observable}'s emissions and notifications on a specified {@link Scheduler}, + * asynchronously with an unbounded buffer with {@link Flowable#bufferSize()} "island size" and optionally delays {@code onError} notifications. *

- * + * *

- * This operator keeps emitting as many signals as it can on the given Scheduler's Worker thread, + * This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s worker thread, * which may result in a longer than expected occupation of this thread. In other terms, * it does not allow per-signal fairness in case the worker runs on a shared underlying thread. * If such fairness and signal/work interleaving is preferred, use the delay operator with zero time instead. *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
*

"Island size" indicates how large chunks the unbounded buffer allocates to store the excess elements waiting to be consumed * on the other side of the asynchronous boundary. * * @param scheduler - * the {@link Scheduler} to notify {@link Observer}s on + * the {@code Scheduler} to notify {@link Observer}s on * @param delayError - * indicates if the onError notification may not cut ahead of onNext notification on the other side of the - * scheduling boundary. If true a sequence ending in onError will be replayed in the same order as was received - * from upstream - * @return the source ObservableSource modified so that its {@link Observer}s are notified on the specified - * {@link Scheduler} + * indicates if the {@code onError} notification may not cut ahead of {@code onNext} notification on the other side of the + * scheduling boundary. If {@code true}, a sequence ending in {@code onError} will be replayed in the same order as was received + * from the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -9855,36 +10618,38 @@ public final Observable observeOn(Scheduler scheduler) { */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable observeOn(Scheduler scheduler, boolean delayError) { + @NonNull + public final Observable observeOn(@NonNull Scheduler scheduler, boolean delayError) { return observeOn(scheduler, delayError, bufferSize()); } /** - * Modifies an ObservableSource to perform its emissions and notifications on a specified {@link Scheduler}, - * asynchronously with an unbounded buffer of configurable "island size" and optionally delays onError notifications. + * Returns an {@code Observable} to perform the current {@code Observable}'s emissions and notifications on a specified {@link Scheduler}, + * asynchronously with an unbounded buffer of configurable "island size" and optionally delays {@code onError} notifications. *

- * + * *

- * This operator keeps emitting as many signals as it can on the given Scheduler's Worker thread, + * This operator keeps emitting as many signals as it can on the given {@code Scheduler}'s worker thread, * which may result in a longer than expected occupation of this thread. In other terms, * it does not allow per-signal fairness in case the worker runs on a shared underlying thread. * If such fairness and signal/work interleaving is preferred, use the delay operator with zero time instead. *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
*

"Island size" indicates how large chunks the unbounded buffer allocates to store the excess elements waiting to be consumed * on the other side of the asynchronous boundary. Values below 16 are not recommended in performance sensitive scenarios. * * @param scheduler - * the {@link Scheduler} to notify {@link Observer}s on + * the {@code Scheduler} to notify {@link Observer}s on * @param delayError - * indicates if the onError notification may not cut ahead of onNext notification on the other side of the - * scheduling boundary. If true a sequence ending in onError will be replayed in the same order as was received + * indicates if the {@code onError} notification may not cut ahead of {@code onNext} notification on the other side of the + * scheduling boundary. If {@code true} a sequence ending in {@code onError} will be replayed in the same order as was received * from upstream * @param bufferSize the size of the buffer. - * @return the source ObservableSource modified so that its {@link Observer}s are notified on the specified - * {@link Scheduler} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -9894,16 +10659,17 @@ public final Observable observeOn(Scheduler scheduler, boolean delayError) { */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable observeOn(Scheduler scheduler, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final Observable observeOn(@NonNull Scheduler scheduler, boolean delayError, int bufferSize) { + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableObserveOn(this, scheduler, delayError, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableObserveOn<>(this, scheduler, delayError, bufferSize)); } /** - * Filters the items emitted by an ObservableSource, only emitting those of the specified type. + * Filters the items emitted by the current {@code Observable}, only emitting those of the specified type. *

- * + * *

*
Scheduler:
*
{@code ofType} does not operate by default on a particular {@link Scheduler}.
@@ -9911,31 +10677,76 @@ public final Observable observeOn(Scheduler scheduler, boolean delayError, in * * @param the output type * @param clazz - * the class type to filter the items emitted by the source ObservableSource - * @return an Observable that emits items from the source ObservableSource of type {@code clazz} + * the class type to filter the items emitted by the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code clazz} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable ofType(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + @NonNull + public final <@NonNull U> Observable ofType(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return filter(Functions.isInstanceOf(clazz)).cast(clazz); } /** - * Instructs an ObservableSource to pass control to another ObservableSource rather than invoking - * {@link Observer#onError onError} if it encounters an error. + * Returns an {@code Observable} instance that if the current {@code Observable} emits an error, it will emit an {@code onComplete} + * and swallow the throwable. + *

+ * + *

+ *
Scheduler:
+ *
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code Observable} instance + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Observable onErrorComplete() { + return onErrorComplete(Functions.alwaysTrue()); + } + + /** + * Returns an {@code Observable} instance that if the current {@code Observable} emits an error and the predicate returns + * {@code true}, it will emit an {@code onComplete} and swallow the throwable. + *

+ * + *

+ *
Scheduler:
+ *
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param predicate the predicate to call when an {@link Throwable} is emitted which should return {@code true} + * if the {@code Throwable} should be swallowed and replaced with an {@code onComplete}. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code predicate} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Observable onErrorComplete(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + + return RxJavaPlugins.onAssembly(new ObservableOnErrorComplete<>(this, predicate)); + } + + /** + * Resumes the flow with an {@link ObservableSource} returned for the failure {@link Throwable} of the current {@code Observable} by a + * function instead of signaling the error via {@code onError}. *

- * + * *

- * By default, when an ObservableSource encounters an error that prevents it from emitting the expected item to - * its {@link Observer}, the ObservableSource invokes its Observer's {@code onError} method, and then quits - * without invoking any more of its Observer's methods. The {@code onErrorResumeNext} method changes this - * behavior. If you pass a function that returns an ObservableSource ({@code resumeFunction}) to - * {@code onErrorResumeNext}, if the original ObservableSource encounters an error, instead of invoking its - * Observer's {@code onError} method, it will instead relinquish control to the ObservableSource returned from - * {@code resumeFunction}, which will invoke the Observer's {@link Observer#onNext onNext} method if it is - * able to do so. In such a case, because no ObservableSource necessarily invokes {@code onError}, the Observer + * By default, when an {@code ObservableSource} encounters an error that prevents it from emitting the expected item to + * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits + * without invoking any more of its {@code Observer}'s methods. The {@code onErrorResumeNext} method changes this + * behavior. If you pass a function that returns an {@code ObservableSource} ({@code resumeFunction}) to + * {@code onErrorResumeNext}, if the original {@code ObservableSource} encounters an error, instead of invoking its + * {@code Observer}'s {@code onError} method, it will instead relinquish control to the {@code ObservableSource} returned from + * {@code resumeFunction}, which will invoke the {@code Observer}'s {@link Observer#onNext onNext} method if it is + * able to do so. In such a case, because no {@code ObservableSource} necessarily invokes {@code onError}, the {@code 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 @@ -9945,33 +10756,35 @@ public final Observable ofType(final Class clazz) { *

{@code onErrorResumeNext} does not operate by default on a particular {@link Scheduler}.
*
* - * @param resumeFunction - * a function that returns an ObservableSource that will take over if the source ObservableSource encounters + * @param fallbackSupplier + * a function that returns an {@code ObservableSource} that will take over if the current {@code Observable} encounters * an error - * @return the original ObservableSource, with appropriately modified behavior + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code fallbackSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable onErrorResumeNext(Function> resumeFunction) { - ObjectHelper.requireNonNull(resumeFunction, "resumeFunction is null"); - return RxJavaPlugins.onAssembly(new ObservableOnErrorNext(this, resumeFunction)); + @NonNull + public final Observable onErrorResumeNext(@NonNull Function> fallbackSupplier) { + Objects.requireNonNull(fallbackSupplier, "fallbackSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableOnErrorNext<>(this, fallbackSupplier)); } /** - * Instructs an ObservableSource to pass control to another ObservableSource rather than invoking - * {@link Observer#onError onError} if it encounters an error. + * Resumes the flow with the given {@link ObservableSource} when the current {@code Observable} fails instead of + * signaling the error via {@code onError}. *

- * + * *

- * By default, when an ObservableSource encounters an error that prevents it from emitting the expected item to - * its {@link Observer}, the ObservableSource invokes its Observer's {@code onError} method, and then quits - * without invoking any more of its Observer's methods. The {@code onErrorResumeWith} method changes this - * behavior. If you pass another ObservableSource ({@code next}) to an ObservableSource's - * {@code onErrorResumeWith} method, if the original ObservableSource encounters an error, instead of invoking its - * Observer's {@code onError} method, it will instead relinquish control to {@code next} which - * will invoke the Observer's {@link Observer#onNext onNext} method if it is able to do so. In such a case, - * because no ObservableSource necessarily invokes {@code onError}, the Observer may never know that an error + * By default, when an {@code ObservableSource} encounters an error that prevents it from emitting the expected item to + * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits + * without invoking any more of its {@code Observer}'s methods. The {@code onErrorResumeWith} method changes this + * behavior. If you pass another {@code ObservableSource} ({@code next}) to an {@code ObservableSource}'s + * {@code onErrorResumeWith} method, if the original {@code ObservableSource} encounters an error, instead of invoking its + * {@code Observer}'s {@code onError} method, it will instead relinquish control to {@code next} which + * will invoke the {@code Observer}'s {@link Observer#onNext onNext} method if it is able to do so. In such a case, + * because no {@code ObservableSource} necessarily invokes {@code onError}, the {@code 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 @@ -9981,30 +10794,32 @@ public final Observable onErrorResumeNext(Function{@code onErrorResumeWith} does not operate by default on a particular {@link Scheduler}. *

* - * @param next - * the next ObservableSource source that will take over if the source ObservableSource encounters + * @param fallback + * the next {@code ObservableSource} source that will take over if the current {@code Observable} encounters * an error - * @return the original ObservableSource, with appropriately modified behavior + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code fallback} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable onErrorResumeWith(final ObservableSource next) { - ObjectHelper.requireNonNull(next, "next is null"); - return onErrorResumeNext(Functions.justFunction(next)); + @NonNull + public final Observable onErrorResumeWith(@NonNull ObservableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return onErrorResumeNext(Functions.justFunction(fallback)); } /** - * Instructs an ObservableSource to emit an item (returned by a specified function) rather than invoking - * {@link Observer#onError onError} if it encounters an error. + * Ends the flow with a last item returned by a function for the {@link Throwable} error signaled by the current + * {@code Observable} instead of signaling the error via {@code onError}. *

- * + * *

- * By default, when an ObservableSource encounters an error that prevents it from emitting the expected item to - * its {@link Observer}, the ObservableSource invokes its Observer's {@code onError} method, and then quits - * without invoking any more of its Observer's methods. The {@code onErrorReturn} method changes this - * behavior. If you pass a function ({@code resumeFunction}) to an ObservableSource's {@code onErrorReturn} - * method, if the original ObservableSource encounters an error, instead of invoking its Observer's + * By default, when an {@link ObservableSource} encounters an error that prevents it from emitting the expected item to + * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits + * without invoking any more of its {@code Observer}'s methods. The {@code onErrorReturn} method changes this + * behavior. If you pass a function ({@code resumeFunction}) to an {@code ObservableSource}'s {@code onErrorReturn} + * method, if the original {@code ObservableSource} encounters an error, instead of invoking its {@code Observer}'s * {@code onError} method, it will instead emit the return value of {@code resumeFunction}. *

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -10014,30 +10829,31 @@ public final Observable onErrorResumeWith(final ObservableSource *

{@code onErrorReturn} does not operate by default on a particular {@link Scheduler}.
*
* - * @param valueSupplier - * a function that returns a single value that will be emitted along with a regular onComplete in case - * the current Observable signals an onError event - * @return the original ObservableSource with appropriately modified behavior + * @param itemSupplier + * a function that returns a single value that will be emitted along with a regular {@code onComplete} in case + * the current {@code Observable} signals an {@code onError} event + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code itemSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable onErrorReturn(Function valueSupplier) { - ObjectHelper.requireNonNull(valueSupplier, "valueSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableOnErrorReturn(this, valueSupplier)); + @NonNull + public final Observable onErrorReturn(@NonNull Function itemSupplier) { + Objects.requireNonNull(itemSupplier, "itemSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableOnErrorReturn<>(this, itemSupplier)); } /** - * Instructs an ObservableSource to emit an item (returned by a specified function) rather than invoking - * {@link Observer#onError onError} if it encounters an error. + * Ends the flow with the given last item when the current {@code Observable} fails instead of signaling the error via {@code onError}. *

- * + * *

- * By default, when an ObservableSource encounters an error that prevents it from emitting the expected item to - * its {@link Observer}, the ObservableSource invokes its Observer's {@code onError} method, and then quits - * without invoking any more of its Observer's methods. The {@code onErrorReturn} method changes this - * behavior. If you pass a function ({@code resumeFunction}) to an ObservableSource's {@code onErrorReturn} - * method, if the original ObservableSource encounters an error, instead of invoking its Observer's + * By default, when an {@link ObservableSource} encounters an error that prevents it from emitting the expected item to + * its {@link Observer}, the {@code ObservableSource} invokes its {@code Observer}'s {@code onError} method, and then quits + * without invoking any more of its {@code Observer}'s methods. The {@code onErrorReturn} method changes this + * behavior. If you pass a function ({@code resumeFunction}) to an {@code ObservableSource}'s {@code onErrorReturn} + * method, if the original {@code ObservableSource} encounters an error, instead of invoking its {@code Observer}'s * {@code onError} method, it will instead emit the return value of {@code resumeFunction}. *

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -10048,61 +10864,64 @@ public final Observable onErrorReturn(Function * * @param item - * the value that is emitted along with a regular onComplete in case the current - * Observable signals an exception - * @return the original ObservableSource with appropriately modified behavior + * the value that is emitted along with a regular {@code onComplete} in case the current + * {@code Observable} signals an exception + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable onErrorReturnItem(final T item) { - ObjectHelper.requireNonNull(item, "item is null"); + @NonNull + public final Observable onErrorReturnItem(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); return onErrorReturn(Functions.justFunction(item)); } /** - * Nulls out references to the upstream producer and downstream Observer if - * the sequence is terminated or downstream calls dispose(). + * Nulls out references to the upstream producer and downstream {@link Observer} if + * the sequence is terminated or downstream calls {@code dispose()}. *

- * + * *

*
Scheduler:
*
{@code onTerminateDetach} does not operate by default on a particular {@link Scheduler}.
*
- * @return an Observable which nulls out references to the upstream producer and downstream Observer if - * the sequence is terminated or downstream calls dispose() + * @return the new {@code Observable} instance + * the sequence is terminated or downstream calls {@code dispose()} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable onTerminateDetach() { - return RxJavaPlugins.onAssembly(new ObservableDetach(this)); + return RxJavaPlugins.onAssembly(new ObservableDetach<>(this)); } /** - * Returns a {@link ConnectableObservable}, which is a variety of ObservableSource that waits until its + * Returns a {@link ConnectableObservable}, which is a variety of {@link ObservableSource} that waits until its * {@link ConnectableObservable#connect connect} method is called before it begins emitting items to those * {@link Observer}s that have subscribed to it. *

- * + * *

*
Scheduler:
*
{@code publish} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link ConnectableObservable} that upon connection causes the source ObservableSource to emit items - * to its {@link Observer}s + * @return the new {@code ConnectableObservable} instance * @see ReactiveX operators documentation: Publish */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final ConnectableObservable publish() { - return RxJavaPlugins.onAssembly(new ObservablePublish(this)); + return RxJavaPlugins.onAssembly(new ObservablePublish<>(this)); } /** - * Returns an Observable that emits the results of invoking a specified selector on items emitted by a - * {@link ConnectableObservable} that shares a single subscription to the underlying sequence. + * Returns an {@code Observable} that emits the results of invoking a specified selector on items emitted by a + * {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} sequence. *

* *

@@ -10111,28 +10930,30 @@ public final ConnectableObservable publish() { *
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a function that can use the multicasted source sequence as many times as needed, without - * causing multiple subscriptions to the source sequence. Observers to the given source will + * causing multiple subscriptions to the source sequence. {@link Observer}s 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 {@link ConnectableObservable} that shares a single subscription to the underlying sequence + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} is {@code null} * @see ReactiveX operators documentation: Publish */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable publish(Function, ? extends ObservableSource> selector) { - ObjectHelper.requireNonNull(selector, "selector is null"); - return RxJavaPlugins.onAssembly(new ObservablePublishSelector(this, selector)); + @NonNull + public final <@NonNull R> Observable publish(@NonNull Function, ? extends ObservableSource> selector) { + Objects.requireNonNull(selector, "selector is null"); + return RxJavaPlugins.onAssembly(new ObservablePublishSelector<>(this, selector)); } /** - * Returns a Maybe that applies a specified accumulator function to the first item emitted by a source - * ObservableSource, then feeds the result of that function along with the second item emitted by the source - * ObservableSource into the same function, and so on until all items have been emitted by the finite source ObservableSource, + * Returns a {@link Maybe} that applies a specified accumulator function to the first item emitted by the current + * {@code Observable}, then feeds the result of that function along with the second item emitted by the current + * {@code Observable} into the same function, and so on until all items have been emitted by the current and finite {@code Observable}, * and emits the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -10140,40 +10961,41 @@ public final Observable publish(Function, ? extends *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code reduce} does not operate by default on a particular {@link Scheduler}.
*
* * @param reducer - * an accumulator function to be invoked on each item emitted by the source ObservableSource, whose + * an accumulator function to be invoked on each item emitted by the current {@code Observable}, whose * result will be used in the next accumulator call - * @return a Maybe that emits a single item that is the result of accumulating the items emitted by - * the source ObservableSource + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code reducer} is {@code null} * @see ReactiveX operators documentation: Reduce * @see Wikipedia: Fold (higher-order function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe reduce(BiFunction reducer) { - ObjectHelper.requireNonNull(reducer, "reducer is null"); - return RxJavaPlugins.onAssembly(new ObservableReduceMaybe(this, reducer)); + @NonNull + public final Maybe reduce(@NonNull BiFunction reducer) { + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new ObservableReduceMaybe<>(this, reducer)); } /** - * Returns a Single that applies a specified accumulator function to the first item emitted by a source - * ObservableSource and a specified seed value, then feeds the result of that function along with the second item - * emitted by an ObservableSource into the same function, and so on until all items have been emitted by the - * finite source ObservableSource, emitting the final result from the final call to your function as its sole item. + * Returns a {@link Single} that applies a specified accumulator function to the first item emitted by the current + * {@code Observable} and a specified seed value, then feeds the result of that function along with the second item + * emitted by the current {@code Observable} into the same function, and so on until all items have been emitted by the + * current and finite {@code Observable}, emitting the final result from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method * that does a similar operation on lists. *

- * Note that the {@code seed} is shared among all subscribers to the resulting ObservableSource + * Note that the {@code seed} is shared among all subscribers to the resulting {@code Observable} * and may cause problems if it is mutable. To make sure each subscriber gets its own value, defer * the application of this operator via {@link #defer(Supplier)}: *


@@ -10193,7 +11015,7 @@ public final Maybe reduce(BiFunction reducer) {
      * 

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code reduce} does not operate by default on a particular {@link Scheduler}.
@@ -10203,30 +11025,31 @@ public final Maybe reduce(BiFunction reducer) { * @param seed * the initial (seed) accumulator value * @param reducer - * an accumulator function to be invoked on each item emitted by the source ObservableSource, the + * an accumulator function to be invoked on each item emitted by the current {@code Observable}, the * result of which will be used in the next accumulator call - * @return a Single that emits a single item that is the result of accumulating the output from the - * items emitted by the source ObservableSource + * @return the new {@code Single} instance + * @throws NullPointerException if {@code seed} or {@code reducer} is {@code null} * @see ReactiveX operators documentation: Reduce * @see Wikipedia: Fold (higher-order function) * @see #reduceWith(Supplier, BiFunction) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single reduce(R seed, BiFunction reducer) { - ObjectHelper.requireNonNull(seed, "seed is null"); - ObjectHelper.requireNonNull(reducer, "reducer is null"); - return RxJavaPlugins.onAssembly(new ObservableReduceSeedSingle(this, seed, reducer)); + @NonNull + public final <@NonNull R> Single reduce(R seed, @NonNull BiFunction reducer) { + Objects.requireNonNull(seed, "seed is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new ObservableReduceSeedSingle<>(this, seed, reducer)); } /** - * Returns a Single that applies a specified accumulator function to the first item emitted by a source - * ObservableSource and a seed value derived from calling a specified seedSupplier, then feeds the result - * of that function along with the second item emitted by an ObservableSource into the same function, - * and so on until all items have been emitted by the finite source ObservableSource, emitting the final result + * Returns a {@link Single} that applies a specified accumulator function to the first item emitted by the current + * {@code Observable} and a seed value derived from calling a specified {@code seedSupplier}, then feeds the result + * of that function along with the second item emitted by the current {@code Observable} into the same function, + * and so on until all items have been emitted by the current and finite {@code Observable}, emitting the final result * from the final call to your function as its sole item. *

- * + * *

* This technique, which is called "reduce" here, is sometimes called "aggregate," "fold," "accumulate," * "compress," or "inject" in other programming contexts. Groovy, for instance, has an {@code inject} method @@ -10234,7 +11057,7 @@ public final Single reduce(R seed, BiFunction reducer) { *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulator object to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code reduceWith} does not operate by default on a particular {@link Scheduler}.
@@ -10242,25 +11065,26 @@ public final Single reduce(R seed, BiFunction reducer) { * * @param the accumulator and output value type * @param seedSupplier - * the Supplier that provides the initial (seed) accumulator value for each individual Observer + * the {@link Supplier} that provides the initial (seed) accumulator value for each individual {@link Observer} * @param reducer - * an accumulator function to be invoked on each item emitted by the source ObservableSource, the + * an accumulator function to be invoked on each item emitted by the current {@code Observable}, the * result of which will be used in the next accumulator call - * @return a Single that emits a single item that is the result of accumulating the output from the - * items emitted by the source ObservableSource + * @return the new {@code Single} instance + * @throws NullPointerException if {@code seedSupplier} or {@code reducer} is {@code null} * @see ReactiveX operators documentation: Reduce * @see Wikipedia: Fold (higher-order function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single reduceWith(Supplier seedSupplier, BiFunction reducer) { - ObjectHelper.requireNonNull(seedSupplier, "seedSupplier is null"); - ObjectHelper.requireNonNull(reducer, "reducer is null"); - return RxJavaPlugins.onAssembly(new ObservableReduceWithSingle(this, seedSupplier, reducer)); + @NonNull + public final <@NonNull R> Single reduceWith(@NonNull Supplier seedSupplier, @NonNull BiFunction reducer) { + Objects.requireNonNull(seedSupplier, "seedSupplier is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new ObservableReduceWithSingle<>(this, seedSupplier, reducer)); } /** - * Returns an Observable that repeats the sequence of items emitted by the source ObservableSource indefinitely. + * Returns an {@code Observable} that repeats the sequence of items emitted by the current {@code Observable} indefinitely. *

* *

@@ -10268,17 +11092,18 @@ public final Single reduceWith(Supplier seedSupplier, BiFunction{@code repeat} does not operate by default on a particular {@link Scheduler}. *
* - * @return an Observable that emits the items emitted by the source ObservableSource repeatedly and in sequence + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable repeat() { return repeat(Long.MAX_VALUE); } /** - * Returns an Observable that repeats the sequence of items emitted by the source ObservableSource at most + * Returns an {@code Observable} that repeats the sequence of items emitted by the current {@code Observable} at most * {@code count} times. *

* @@ -10288,16 +11113,16 @@ public final Observable repeat() { *

* * @param times - * the number of times the source ObservableSource items are repeated, a count of 0 will yield an empty + * the number of times the current {@code 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 ObservableSource at most - * {@code count} times + * @return the new {@code Observable} instance * @throws IllegalArgumentException - * if {@code count} is less than zero + * if {@code times} is negative * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable repeat(long times) { if (times < 0) { throw new IllegalArgumentException("times >= 0 required but it was " + times); @@ -10305,86 +11130,89 @@ public final Observable repeat(long times) { if (times == 0) { return empty(); } - return RxJavaPlugins.onAssembly(new ObservableRepeat(this, times)); + return RxJavaPlugins.onAssembly(new ObservableRepeat<>(this, times)); } /** - * Returns an Observable that repeats the sequence of items emitted by the source ObservableSource until - * the provided stop function returns true. + * Returns an {@code Observable} that repeats the sequence of items emitted by the current {@code Observable} until + * the provided stop function returns {@code true}. *

- * + * *

*
Scheduler:
*
{@code repeatUntil} does not operate by default on a particular {@link Scheduler}.
*
* * @param stop - * a boolean supplier that is called when the current Observable completes; - * if it returns true, the returned Observable completes; if it returns false, - * the upstream Observable is resubscribed. - * @return the new Observable instance + * a boolean supplier that is called when the current {@code Observable} completes; + * if it returns {@code true}, the returned {@code Observable} completes; if it returns {@code false}, + * the current {@code Observable} is resubscribed. + * @return the new {@code Observable} instance * @throws NullPointerException - * if {@code stop} is null + * if {@code stop} is {@code null} * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable repeatUntil(BooleanSupplier stop) { - ObjectHelper.requireNonNull(stop, "stop is null"); - return RxJavaPlugins.onAssembly(new ObservableRepeatUntil(this, stop)); + @NonNull + public final Observable repeatUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); + return RxJavaPlugins.onAssembly(new ObservableRepeatUntil<>(this, stop)); } /** - * Returns an Observable that emits the same values as the source ObservableSource with the exception of an + * Returns an {@code Observable} that emits the same values as the current {@code Observable} with the exception of an * {@code onComplete}. An {@code onComplete} notification from the source will result in the emission of - * a {@code void} item to the ObservableSource provided as an argument to the {@code notificationHandler} - * function. If that ObservableSource calls {@code onComplete} or {@code onError} then {@code repeatWhen} will - * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, this ObservableSource will - * resubscribe to the source ObservableSource. + * a {@code void} item to the {@link ObservableSource} provided as an argument to the {@code notificationHandler} + * function. If that {@code ObservableSource} calls {@code onComplete} or {@code onError} then {@code repeatWhen} will + * call {@code onComplete} or {@code onError} on the child subscription. Otherwise, the current {@code Observable} + * will be resubscribed. *

- * + * *

*
Scheduler:
*
{@code repeatWhen} does not operate by default on a particular {@link Scheduler}.
*
* * @param handler - * receives an ObservableSource of notifications with which a user can complete or error, aborting the repeat. - * @return the source ObservableSource modified with repeat logic + * receives an {@code ObservableSource} of notifications with which a user can complete or error, aborting the repeat. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code handler} is {@code null} * @see ReactiveX operators documentation: Repeat */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable repeatWhen(final Function, ? extends ObservableSource> handler) { - ObjectHelper.requireNonNull(handler, "handler is null"); - return RxJavaPlugins.onAssembly(new ObservableRepeatWhen(this, handler)); + @NonNull + public final Observable repeatWhen(@NonNull Function, ? extends ObservableSource> handler) { + Objects.requireNonNull(handler, "handler is null"); + return RxJavaPlugins.onAssembly(new ObservableRepeatWhen<>(this, handler)); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the underlying ObservableSource - * that will replay all of its items and notifications to any future {@link Observer}. A Connectable - * ObservableSource resembles an ordinary ObservableSource, except that it does not begin emitting items when it is + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} + * that will replay all of its items and notifications to any future {@link Observer}. A connectable + * {@code Observable} resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

*
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link ConnectableObservable} that upon connection causes the source ObservableSource to emit its - * items to its {@link Observer}s + * @return the new {@code ConnectableObservable} instance * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final ConnectableObservable replay() { return ObservableReplay.createFrom(this); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on the items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource. + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on the items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}. *

* *

@@ -10393,96 +11221,99 @@ public final ConnectableObservable replay() { *
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource - * @return an Observable that emits items that are the results of invoking the selector on a - * {@link ConnectableObservable} that shares a single subscription to the source ObservableSource + * causing multiple subscriptions to the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable replay(Function, ? extends ObservableSource> selector) { - ObjectHelper.requireNonNull(selector, "selector is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector) { + Objects.requireNonNull(selector, "selector is null"); return ObservableReplay.multicastSelector(ObservableInternalHelper.replaySupplier(this), selector); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying {@code bufferSize} notifications. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param bufferSize - * the buffer size that limits the number of items the connectable ObservableSource can replay - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource - * replaying no more than {@code bufferSize} items + * the buffer size that limits the number of items the connectable {@code Observable} can replay + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(Function, int, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable replay(Function, ? extends ObservableSource> selector, final int bufferSize) { - ObjectHelper.requireNonNull(selector, "selector is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, int bufferSize) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return ObservableReplay.multicastSelector(ObservableInternalHelper.replaySupplier(this, bufferSize, false), selector); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying {@code bufferSize} notifications. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Scheduler:
*
This version of {@code replay} does not operate by default on a particular {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * the selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param bufferSize - * the buffer size that limits the number of items the connectable ObservableSource can replay + * the buffer size that limits the number of items the connectable {@code Observable} can replay * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource - * replaying no more than {@code bufferSize} items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable replay(Function, ? extends ObservableSource> selector, final int bufferSize, boolean eagerTruncate) { - ObjectHelper.requireNonNull(selector, "selector is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, int bufferSize, boolean eagerTruncate) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return ObservableReplay.multicastSelector(ObservableInternalHelper.replaySupplier(this, bufferSize, eagerTruncate), selector); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying no more than {@code bufferSize} items that were emitted within a specified time window. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than @@ -10495,207 +11326,207 @@ public final Observable replay(Function, ? extends *

* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param bufferSize - * the buffer size that limits the number of items the connectable ObservableSource can replay + * the buffer size that limits the number of items the connectable {@code Observable} can replay * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, and - * replays no more than {@code bufferSize} items that were emitted within the window defined by - * {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} or {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable replay(Function, ? extends ObservableSource> selector, int bufferSize, long time, TimeUnit unit) { + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, int bufferSize, long time, @NonNull TimeUnit unit) { return replay(selector, bufferSize, time, unit, Schedulers.computation()); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying no more than {@code bufferSize} items that were emitted within a specified time window. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param bufferSize - * the buffer size that limits the number of items the connectable ObservableSource can replay + * the buffer size that limits the number of items the connectable {@code Observable} can replay * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} * @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 {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, and - * replays no more than {@code bufferSize} items that were emitted within the window defined by - * {@code time} + * the {@code Scheduler} that is the time source for the window + * @return the new {@code Observable} instance * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * if {@code bufferSize} is non-positive + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(Function, int, long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable replay(Function, ? extends ObservableSource> selector, final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(selector, "selector is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.multicastSelector( ObservableInternalHelper.replaySupplier(this, bufferSize, time, unit, scheduler, false), selector); } + /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying no more than {@code bufferSize} items that were emitted within a specified time window. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param bufferSize - * the buffer size that limits the number of items the connectable ObservableSource can replay + * the buffer size that limits the number of items the connectable {@code Observable} can replay * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window + * the {@code Scheduler} that is the time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, and - * replays no more than {@code bufferSize} items that were emitted within the window defined by - * {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable replay(Function, ? extends ObservableSource> selector, final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.requireNonNull(selector, "selector is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(selector, "selector is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.multicastSelector( ObservableInternalHelper.replaySupplier(this, bufferSize, time, unit, scheduler, eagerTruncate), selector); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, - * replaying all items that were emitted within the window defined by {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector} or {@code unit} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable replay(Function, ? extends ObservableSource> selector, long time, TimeUnit unit) { + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, long time, @NonNull TimeUnit unit) { return replay(selector, time, unit, Schedulers.computation()); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param time * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} * @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 {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, - * replaying all items that were emitted within the window defined by {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay * @see #replay(Function, long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable replay(Function, ? extends ObservableSource> selector, final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(selector, "selector is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(selector, "selector is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.multicastSelector(ObservableInternalHelper.replaySupplier(this, time, unit, scheduler, false), selector); } /** - * Returns an Observable that emits items that are the results of invoking a specified selector on items - * emitted by a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, + * Returns an {@code Observable} that emits items that are the results of invoking a specified selector on items + * emitted by a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable}, * replaying all items that were emitted within a specified time window. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
*
* * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param selector * a selector function, which can use the multicasted sequence as many times as needed, without - * causing multiple subscriptions to the ObservableSource + * causing multiple subscriptions to the current {@code Observable} * @param time * the duration of the window in which the replayed items must have been emitted * @param unit @@ -10703,29 +11534,29 @@ public final Observable replay(Function, ? extends * @param scheduler * the scheduler that is the time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given age, the + * if {@code true}, whenever the internal buffer is truncated to the given age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return an Observable that emits items that are the results of invoking the selector on items emitted by - * a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource, - * replaying all items that were emitted within the window defined by {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code selector}, {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable replay(Function, ? extends ObservableSource> selector, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.requireNonNull(selector, "selector is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final <@NonNull R> Observable replay(@NonNull Function, ? extends ObservableSource> selector, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(selector, "selector is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.multicastSelector(ObservableInternalHelper.replaySupplier(this, time, unit, scheduler, eagerTruncate), selector); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource that - * replays at most {@code bufferSize} items emitted by that ObservableSource. A Connectable ObservableSource resembles - * an ordinary ObservableSource, except that it does not begin emitting items when it is subscribed to, but only + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} that + * replays at most {@code bufferSize} items emitted by the current {@code Observable}. A connectable {@code Observable} resembles + * an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -10738,25 +11569,26 @@ public final Observable replay(Function, ? extends * * @param bufferSize * the buffer size that limits the number of items that can be replayed - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays at most {@code bufferSize} items emitted by that ObservableSource + * @return the new {@code ConnectableObservable} instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(int, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final ConnectableObservable replay(final int bufferSize) { + @NonNull + public final ConnectableObservable replay(int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return ObservableReplay.create(this, bufferSize, false); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource that - * replays at most {@code bufferSize} items emitted by that ObservableSource. A Connectable ObservableSource resembles - * an ordinary ObservableSource, except that it does not begin emitting items when it is subscribed to, but only + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} that + * replays at most {@code bufferSize} items emitted by the current {@code Observable}. A connectable {@code Observable} resembles + * an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, but only * when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -10769,26 +11601,27 @@ public final ConnectableObservable replay(final int bufferSize) { * @param bufferSize * the buffer size that limits the number of items that can be replayed * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays at most {@code bufferSize} items emitted by that ObservableSource + * @return the new {@code ConnectableObservable} instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final ConnectableObservable replay(final int bufferSize, boolean eagerTruncate) { + @NonNull + public final ConnectableObservable replay(int bufferSize, boolean eagerTruncate) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); return ObservableReplay.create(this, bufferSize, eagerTruncate); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays at most {@code bufferSize} items that were emitted during a specified time window. A Connectable - * ObservableSource resembles an ordinary ObservableSource, except that it does not begin emitting items when it is + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} and + * replays at most {@code bufferSize} items that were emitted during a specified time window. A connectable + * {@code Observable} resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is * subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -10805,22 +11638,23 @@ public final ConnectableObservable replay(final int bufferSize, boolean eager * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays at most {@code bufferSize} items that were emitted during the window defined by - * {@code time} + * @return the new {@code ConnectableObservable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(int, long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final ConnectableObservable replay(int bufferSize, long time, TimeUnit unit) { + @NonNull + public final ConnectableObservable replay(int bufferSize, long time, @NonNull TimeUnit unit) { return replay(bufferSize, time, unit, Schedulers.computation()); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} and * that replays a maximum of {@code bufferSize} items that are emitted within a specified time window. A - * Connectable ObservableSource resembles an ordinary ObservableSource, except that it does not begin emitting items + * connectable {@code Observable} resembles an ordinary {@code Observable}, except that it does not begin emitting items * when it is subscribed to, but only when its {@code connect} method is called. *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than @@ -10828,7 +11662,7 @@ public final ConnectableObservable replay(int bufferSize, long time, TimeUnit * To ensure no out-of-date or beyond-bufferSize items are referenced, * use the {@link #replay(int, long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. *

- * + * *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -10842,30 +11676,30 @@ public final ConnectableObservable replay(int bufferSize, long time, TimeUnit * the time unit of {@code time} * @param scheduler * the scheduler that is used as a time source for the window - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays at most {@code bufferSize} items that were emitted during the window defined by - * {@code time} + * @return the new {@code ConnectableObservable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay * @see #replay(int, long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableObservable replay(final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler) { + @NonNull + public final ConnectableObservable replay(int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.create(this, time, unit, scheduler, bufferSize, false); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} and * that replays a maximum of {@code bufferSize} items that are emitted within a specified time window. A - * Connectable ObservableSource resembles an ordinary ObservableSource, except that it does not begin emitting items + * connectable {@code Observable} resembles an ordinary {@code Observable}, except that it does not begin emitting items * when it is subscribed to, but only when its {@code connect} method is called. *

- * + * *

* Note that due to concurrency requirements, {@code replay(bufferSize)} may hold strong references to more than * {@code bufferSize} source emissions. @@ -10884,32 +11718,32 @@ public final ConnectableObservable replay(final int bufferSize, final long ti * the time unit of {@code time} * @param scheduler * the scheduler that is used as a time source for the window - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays at most {@code bufferSize} items that were emitted during the window defined by - * {@code time} + * @return the new {@code ConnectableObservable} instance * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @throws IllegalArgumentException - * if {@code bufferSize} is less than zero + * if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableObservable replay(final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { + @NonNull + public final ConnectableObservable replay(int bufferSize, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.create(this, time, unit, scheduler, bufferSize, eagerTruncate); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays all items emitted by that ObservableSource within a specified time window. A Connectable ObservableSource - * resembles an ordinary ObservableSource, except that it does not begin emitting items when it is subscribed to, + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} and + * replays all items emitted by the current {@code Observable} within a specified time window. A connectable {@code Observable} + * resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

*
Scheduler:
*
This version of {@code replay} operates by default on the {@code computation} {@link Scheduler}.
@@ -10919,23 +11753,24 @@ public final ConnectableObservable replay(final int bufferSize, final long ti * the duration of the window in which the replayed items must have been emitted * @param unit * the time unit of {@code time} - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays the items that were emitted during the window defined by {@code time} + * @return the new {@code ConnectableObservable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final ConnectableObservable replay(long time, TimeUnit unit) { + @NonNull + public final ConnectableObservable replay(long time, @NonNull TimeUnit unit) { return replay(time, unit, Schedulers.computation()); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays all items emitted by that ObservableSource within a specified time window. A Connectable ObservableSource - * resembles an ordinary ObservableSource, except that it does not begin emitting items when it is subscribed to, + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} and + * replays all items emitted by the current {@code Observable} within a specified time window. A connectable {@code Observable} + * resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, use the {@link #replay(long, TimeUnit, Scheduler, boolean)} overload with {@code eagerTruncate = true}. @@ -10949,27 +11784,28 @@ public final ConnectableObservable replay(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays the items that were emitted during the window defined by {@code time} + * the {@code Scheduler} that is the time source for the window + * @return the new {@code ConnectableObservable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay * @see #replay(long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableObservable replay(final long time, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final ConnectableObservable replay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.create(this, time, unit, scheduler, false); } /** - * Returns a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays all items emitted by that ObservableSource within a specified time window. A Connectable ObservableSource - * resembles an ordinary ObservableSource, except that it does not begin emitting items when it is subscribed to, + * Returns a {@link ConnectableObservable} that shares a single subscription to the current {@code Observable} and + * replays all items emitted by the current {@code Observable} within a specified time window. A connectable {@code Observable} + * resembles an ordinary {@code Observable}, except that it does not begin emitting items when it is subscribed to, * but only when its {@code connect} method is called. *

- * + * *

* Note that the internal buffer may retain strong references to the oldest item. To ensure no out-of-date items * are referenced, set {@code eagerTruncate = true}. @@ -10983,33 +11819,34 @@ public final ConnectableObservable replay(final long time, final TimeUnit uni * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that is the time source for the window + * the {@code Scheduler} that is the time source for the window * @param eagerTruncate - * if true, whenever the internal buffer is truncated to the given bufferSize/age, the + * if {@code true}, whenever the internal buffer is truncated to the given bufferSize/age, the * oldest item will be guaranteed dereferenced, thus avoiding unexpected retention - * @return a {@link ConnectableObservable} that shares a single subscription to the source ObservableSource and - * replays the items that were emitted during the window defined by {@code time} + * @return the new {@code ConnectableObservable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Replay */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final ConnectableObservable replay(final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final ConnectableObservable replay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean eagerTruncate) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return ObservableReplay.create(this, time, unit, scheduler, eagerTruncate); } /** - * Returns an Observable that mirrors the source ObservableSource, resubscribing to it if it calls {@code onError} + * Returns an {@code Observable} that mirrors the current {@code Observable}, resubscribing to it if it calls {@code onError} * (infinite retry count). *

- * + * *

- * If the source ObservableSource calls {@link Observer#onError}, this method will resubscribe to the source - * ObservableSource rather than propagating the {@code onError} call. + * If the current {@code Observable} calls {@link Observer#onError}, this method will resubscribe to the current + * {@code Observable} rather than propagating the {@code onError} call. *

- * Any and all items emitted by the source ObservableSource will be emitted by the resulting ObservableSource, even - * those emitted during failed subscriptions. For example, if an ObservableSource fails at first but emits + * Any and all items emitted by the current {@code Observable} will be emitted by the resulting {@code Observable}, even + * those emitted during failed subscriptions. For example, if the current {@code Observable} fails at first but emits * {@code [1, 2]} then succeeds the second time and emits {@code [1, 2, 3, 4, 5]} then the complete sequence * of emissions and notifications would be {@code [1, 2, 1, 2, 3, 4, 5, onComplete]}. *

@@ -11017,20 +11854,21 @@ public final ConnectableObservable replay(final long time, final TimeUnit uni *
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the source ObservableSource modified with retry logic + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable retry() { return retry(Long.MAX_VALUE, Functions.alwaysTrue()); } /** - * Returns an Observable that mirrors the source ObservableSource, resubscribing to it if it calls {@code onError} - * and the predicate returns true for that specific exception and retry count. + * Returns an {@code Observable} that mirrors the current {@code Observable}, resubscribing to it if it calls {@code onError} + * and the predicate returns {@code true} for that specific exception and retry count. *

- * + * *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
@@ -11039,30 +11877,32 @@ public final Observable retry() { * @param predicate * the predicate that determines if a resubscription may happen in case of a specific exception * and retry count - * @return the source ObservableSource modified with retry logic + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see #retry() * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable retry(BiPredicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); + @NonNull + public final Observable retry(@NonNull BiPredicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableRetryBiPredicate(this, predicate)); + return RxJavaPlugins.onAssembly(new ObservableRetryBiPredicate<>(this, predicate)); } /** - * Returns an Observable that mirrors the source ObservableSource, resubscribing to it if it calls {@code onError} + * Returns an {@code Observable} that mirrors the current {@code Observable}, resubscribing to it if it calls {@code onError} * up to a specified number of retries. *

- * + * *

- * If the source ObservableSource calls {@link Observer#onError}, this method will resubscribe to the source - * ObservableSource for a maximum of {@code count} resubscriptions rather than propagating the + * If the current {@code Observable} calls {@link Observer#onError}, this method will resubscribe to the current + * {@code Observable} for a maximum of {@code count} resubscriptions rather than propagating the * {@code onError} call. *

- * Any and all items emitted by the source ObservableSource will be emitted by the resulting ObservableSource, even - * those emitted during failed subscriptions. For example, if an ObservableSource fails at first but emits + * Any and all items emitted by the current {@code Observable} will be emitted by the resulting {@code Observable}, even + * those emitted during failed subscriptions. For example, if the current {@code Observable} fails at first but emits * {@code [1, 2]} then succeeds the second time and emits {@code [1, 2, 3, 4, 5]} then the complete sequence * of emissions and notifications would be {@code [1, 2, 1, 2, 3, 4, 5, onComplete]}. *

@@ -11071,84 +11911,93 @@ public final Observable retry(BiPredicate *
* * @param times - * the number of times to resubscribe if the current Observable fails - * @return the source ObservableSource modified with retry logic + * the number of times to resubscribe if the current {@code Observable} fails + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code times} is negative * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable retry(long times) { return retry(times, Functions.alwaysTrue()); } /** - * Retries at most times or until the predicate returns false, whichever happens first. + * Retries at most times or until the predicate returns {@code false}, whichever happens first. *

- * + * *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param times the number of times to resubscribe if the current Observable fails - * @param predicate the predicate called with the failure Throwable and should return true to trigger a retry. - * @return the new Observable instance + * @param times the number of times to resubscribe if the current {@code Observable} fails + * @param predicate the predicate called with the failure {@link Throwable} and should return {@code true} to trigger a retry. + * @throws NullPointerException if {@code predicate} is {@code null} + * @throws IllegalArgumentException if {@code times} is negative + * @return the new {@code Observable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable retry(long times, Predicate predicate) { + @NonNull + public final Observable retry(long times, @NonNull Predicate predicate) { if (times < 0) { throw new IllegalArgumentException("times >= 0 required but it was " + times); } - ObjectHelper.requireNonNull(predicate, "predicate is null"); + Objects.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableRetryPredicate(this, times, predicate)); + return RxJavaPlugins.onAssembly(new ObservableRetryPredicate<>(this, times, predicate)); } /** - * Retries the current Observable if the predicate returns true. + * Retries the current {@code Observable} if the predicate returns {@code true}. *

- * + * *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
* - * @param predicate the predicate that receives the failure Throwable and should return true to trigger a retry. - * @return the new Observable instance + * @param predicate the predicate that receives the failure {@link Throwable} and should return {@code true} to trigger a retry. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable retry(Predicate predicate) { + @NonNull + public final Observable retry(@NonNull Predicate predicate) { return retry(Long.MAX_VALUE, predicate); } /** - * Retries until the given stop function returns true. + * Retries until the given stop function returns {@code true}. *

- * + * *

*
Scheduler:
*
{@code retryUntil} does not operate by default on a particular {@link Scheduler}.
*
- * @param stop the function that should return true to stop retrying - * @return the new Observable instance + * @param stop the function that should return {@code true} to stop retrying + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code stop} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable retryUntil(final BooleanSupplier stop) { - ObjectHelper.requireNonNull(stop, "stop is null"); + @NonNull + public final Observable retryUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); return retry(Long.MAX_VALUE, Functions.predicateReverseFor(stop)); } /** - * Returns an Observable that emits the same values as the source ObservableSource with the exception of an + * Returns an {@code Observable} that emits the same values as the current {@code Observable} with the exception of an * {@code onError}. An {@code onError} notification from the source will result in the emission of a - * {@link Throwable} item to the ObservableSource provided as an argument to the {@code notificationHandler} - * function. If that ObservableSource calls {@code onComplete} or {@code onError} then {@code retry} will call - * {@code onComplete} or {@code onError} on the child subscription. Otherwise, this ObservableSource will - * resubscribe to the source ObservableSource. + * {@link Throwable} item to the {@code Observable} provided as an argument to the {@code notificationHandler} + * function. If that {@code Observable} calls {@code onComplete} or {@code onError} then {@code retry} will call + * {@code onComplete} or {@code onError} on the child subscription. Otherwise, the current {@code Observable} + * will be resubscribed. *

- * + * *

* Example: * @@ -11178,10 +12027,10 @@ public final Observable retryUntil(final BooleanSupplier stop) { * subscribing * }

*

- * Note that the inner {@code ObservableSource} returned by the handler function should signal + * Note that the inner {@link ObservableSource} returned by the handler function should signal * either {@code onNext}, {@code onError} or {@code onComplete} in response to the received * {@code Throwable} to indicate the operator should retry or terminate. If the upstream to - * the operator is asynchronous, signalling onNext followed by onComplete immediately may + * the operator is asynchronous, signaling {@code onNext} followed by {@code onComplete} immediately may * result in the sequence to be completed immediately. Similarly, if this inner * {@code ObservableSource} signals {@code onError} or {@code onComplete} while the upstream is * active, the sequence is terminated with the same signal immediately. @@ -11208,46 +12057,48 @@ public final Observable retryUntil(final BooleanSupplier stop) { *

* * @param handler - * receives an ObservableSource of notifications with which a user can complete or error, aborting the + * receives an {@code Observable} of notifications with which a user can complete or error, aborting the * retry - * @return the source ObservableSource modified with retry logic + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code handler} is {@code null} * @see ReactiveX operators documentation: Retry */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable retryWhen( - final Function, ? extends ObservableSource> handler) { - ObjectHelper.requireNonNull(handler, "handler is null"); - return RxJavaPlugins.onAssembly(new ObservableRetryWhen(this, handler)); + @NonNull Function, ? extends ObservableSource> handler) { + Objects.requireNonNull(handler, "handler is null"); + return RxJavaPlugins.onAssembly(new ObservableRetryWhen<>(this, handler)); } /** - * Subscribes to the current Observable and wraps the given Observer into a SafeObserver - * (if not already a SafeObserver) that - * deals with exceptions thrown by a misbehaving Observer (that doesn't follow the - * Reactive-Streams specification). + * Subscribes to the current {@code Observable} and wraps the given {@link Observer} into a {@link SafeObserver} + * (if not already a {@code SafeObserver}) that + * deals with exceptions thrown by a misbehaving {@code Observer} (that doesn't follow the + * Reactive Streams specification). *
*
Scheduler:
*
{@code safeSubscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param observer the incoming Observer instance - * @throws NullPointerException if s is null + * @param observer the incoming {@code Observer} instance + * @throws NullPointerException if {@code observer} is {@code null} */ @SchedulerSupport(SchedulerSupport.NONE) - public final void safeSubscribe(Observer observer) { - ObjectHelper.requireNonNull(observer, "observer is null"); + public final void safeSubscribe(@NonNull Observer observer) { + Objects.requireNonNull(observer, "observer is null"); if (observer instanceof SafeObserver) { subscribe(observer); } else { - subscribe(new SafeObserver(observer)); + subscribe(new SafeObserver<>(observer)); } } /** - * Returns an Observable that emits the most recently emitted item (if any) emitted by the source ObservableSource + * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} * within periodic time intervals. *

- * + * *

*
Scheduler:
*
{@code sample} operates by default on the {@code computation} {@link Scheduler}.
@@ -11257,22 +12108,23 @@ public final void safeSubscribe(Observer observer) { * the sampling rate * @param unit * the {@link TimeUnit} in which {@code period} is defined - * @return an Observable that emits the results of sampling the items emitted by the source ObservableSource at - * the specified time interval + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see #throttleLast(long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable sample(long period, TimeUnit unit) { + @NonNull + public final Observable sample(long period, @NonNull TimeUnit unit) { return sample(period, unit, Schedulers.computation()); } /** - * Returns an Observable that emits the most recently emitted item (if any) emitted by the source ObservableSource + * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} * within periodic time intervals and optionally emit the very last upstream item when the upstream completes. *

- * + * *

*
Scheduler:
*
{@code sample} operates by default on the {@code computation} {@link Scheduler}.
@@ -11283,30 +12135,31 @@ public final Observable sample(long period, TimeUnit unit) { * the sampling rate * @param unit * the {@link TimeUnit} in which {@code period} is defined - * @return an Observable that emits the results of sampling the items emitted by the source ObservableSource at - * the specified time interval * @param emitLast - * if true and the upstream completes while there is still an unsampled item available, + * if {@code true} and the upstream completes while there is still an unsampled item available, * that item is emitted to downstream before completion - * if false, an unsampled last item is ignored. + * if {@code false}, an unsampled last item is ignored. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see #throttleLast(long, TimeUnit) * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable sample(long period, TimeUnit unit, boolean emitLast) { + @NonNull + public final Observable sample(long period, @NonNull TimeUnit unit, boolean emitLast) { return sample(period, unit, Schedulers.computation(), emitLast); } /** - * Returns an Observable that emits the most recently emitted item (if any) emitted by the source ObservableSource - * within periodic time intervals, where the intervals are defined on a particular Scheduler. + * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} + * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param period @@ -11314,29 +12167,30 @@ public final Observable sample(long period, TimeUnit unit, boolean emitLast) * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param scheduler - * the {@link Scheduler} to use when sampling - * @return an Observable that emits the results of sampling the items emitted by the source ObservableSource at - * the specified time interval + * the {@code Scheduler} to use when sampling + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Sample * @see #throttleLast(long, TimeUnit, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable sample(long period, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableSampleTimed(this, period, unit, scheduler, false)); + @NonNull + public final Observable sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableSampleTimed<>(this, period, unit, scheduler, false, null)); } /** - * Returns an Observable that emits the most recently emitted item (if any) emitted by the source ObservableSource - * within periodic time intervals, where the intervals are defined on a particular Scheduler + * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} + * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler} * and optionally emit the very last upstream item when the upstream completes. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* *

History: 2.0.5 - experimental @@ -11345,89 +12199,130 @@ public final Observable sample(long period, TimeUnit unit, Scheduler schedule * @param unit * the {@link TimeUnit} in which {@code period} is defined * @param scheduler - * the {@link Scheduler} to use when sampling + * the {@code Scheduler} to use when sampling * @param emitLast - * if true and the upstream completes while there is still an unsampled item available, + * if {@code true} and the upstream completes while there is still an unsampled item available, * that item is emitted to downstream before completion - * if false, an unsampled last item is ignored. - * @return an Observable that emits the results of sampling the items emitted by the source ObservableSource at - * the specified time interval + * if {@code false}, an unsampled last item is ignored. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Sample * @see #throttleLast(long, TimeUnit, Scheduler) * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable sample(long period, TimeUnit unit, Scheduler scheduler, boolean emitLast) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableSampleTimed(this, period, unit, scheduler, emitLast)); + @NonNull + public final Observable sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableSampleTimed<>(this, period, unit, scheduler, emitLast, null)); + } + + /** + * Returns an {@code Observable} that emits the most recently emitted item (if any) emitted by the current {@code Observable} + * within periodic time intervals, where the intervals are defined on a particular {@link Scheduler}. + *

+ * + *

+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param period + * the sampling rate + * @param unit + * the {@link TimeUnit} in which {@code period} is defined + * @param scheduler + * the {@code Scheduler} to use when sampling + * @param emitLast + * if {@code true} and the upstream completes while there is still an unsampled item available, + * that item is emitted to downstream before completion + * if {@code false}, an unsampled last item is ignored. + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Sample + * @see #throttleLast(long, TimeUnit, Scheduler) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Observable sample(long period, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new ObservableSampleTimed<>(this, period, unit, scheduler, emitLast, onDropped)); } /** - * Returns an Observable that, when the specified {@code sampler} ObservableSource emits an item or completes, - * emits the most recently emitted item (if any) emitted by the source ObservableSource since the previous - * emission from the {@code sampler} ObservableSource. + * Returns an {@code Observable} that, when the specified {@code sampler} {@link ObservableSource} emits an item or completes, + * emits the most recently emitted item (if any) emitted by the current {@code Observable} since the previous + * emission from the {@code sampler} {@code ObservableSource}. *

- * + * *

*
Scheduler:
*
This version of {@code sample} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the sampler ObservableSource + * @param the element type of the sampler {@code ObservableSource} * @param sampler - * the ObservableSource to use for sampling the source ObservableSource - * @return an Observable that emits the results of sampling the items emitted by this ObservableSource whenever - * the {@code sampler} ObservableSource emits an item or completes + * the {@code ObservableSource} to use for sampling the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sampler} is {@code null} * @see ReactiveX operators documentation: Sample */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable sample(ObservableSource sampler) { - ObjectHelper.requireNonNull(sampler, "sampler is null"); - return RxJavaPlugins.onAssembly(new ObservableSampleWithObservable(this, sampler, false)); + @NonNull + public final <@NonNull U> Observable sample(@NonNull ObservableSource sampler) { + Objects.requireNonNull(sampler, "sampler is null"); + return RxJavaPlugins.onAssembly(new ObservableSampleWithObservable<>(this, sampler, false)); } /** - * Returns an Observable that, when the specified {@code sampler} ObservableSource emits an item or completes, - * emits the most recently emitted item (if any) emitted by the source ObservableSource since the previous - * emission from the {@code sampler} ObservableSource - * and optionally emit the very last upstream item when the upstream or other ObservableSource complete. + * Returns an {@code Observable} that, when the specified {@code sampler} {@link ObservableSource} emits an item or completes, + * emits the most recently emitted item (if any) emitted by the current {@code Observable} since the previous + * emission from the {@code sampler} {@code ObservableSource} + * and optionally emit the very last upstream item when the upstream or other {@code ObservableSource} complete. *

- * + * *

*
Scheduler:
*
This version of {@code sample} does not operate by default on a particular {@link Scheduler}.
*
* *

History: 2.0.5 - experimental - * @param the element type of the sampler ObservableSource + * @param the element type of the sampler {@code ObservableSource} * @param sampler - * the ObservableSource to use for sampling the source ObservableSource + * the {@code ObservableSource} to use for sampling the current {@code Observable} * @param emitLast - * if true and the upstream completes while there is still an unsampled item available, + * if {@code true} and the upstream completes while there is still an unsampled item available, * that item is emitted to downstream before completion - * if false, an unsampled last item is ignored. - * @return an Observable that emits the results of sampling the items emitted by this ObservableSource whenever - * the {@code sampler} ObservableSource emits an item or completes + * if {@code false}, an unsampled last item is ignored. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code sampler} is {@code null} * @see ReactiveX operators documentation: Sample * @since 2.1 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable sample(ObservableSource sampler, boolean emitLast) { - ObjectHelper.requireNonNull(sampler, "sampler is null"); - return RxJavaPlugins.onAssembly(new ObservableSampleWithObservable(this, sampler, emitLast)); + @NonNull + public final <@NonNull U> Observable sample(@NonNull ObservableSource sampler, boolean emitLast) { + Objects.requireNonNull(sampler, "sampler is null"); + return RxJavaPlugins.onAssembly(new ObservableSampleWithObservable<>(this, sampler, emitLast)); } /** - * Returns an Observable that applies a specified accumulator function to the first item emitted by a source - * ObservableSource, then feeds the result of that function along with the second item emitted by the source - * ObservableSource into the same function, and so on until all items have been emitted by the source ObservableSource, - * emitting the result of each of these iterations. + * Returns an {@code Observable} that emits the first value emitted by the current {@code Observable}, then emits one value + * for each subsequent value emitted by the current {@code Observable}. Each emission after the first is the result of + * applying the specified accumulator function to the previous emission and the corresponding value from the current {@code Observable}. *

- * + * *

* This sort of function is sometimes called an accumulator. *

@@ -11436,33 +12331,34 @@ public final Observable sample(ObservableSource sampler, boolean emitL *
* * @param accumulator - * an accumulator function to be invoked on each item emitted by the source ObservableSource, whose + * an accumulator function to be invoked on each item emitted by the current {@code Observable}, whose * result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the * next accumulator call - * @return an Observable that emits the results of each call to the accumulator function + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code accumulator} is {@code null} * @see ReactiveX operators documentation: Scan */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable scan(BiFunction accumulator) { - ObjectHelper.requireNonNull(accumulator, "accumulator is null"); - return RxJavaPlugins.onAssembly(new ObservableScan(this, accumulator)); + @NonNull + public final Observable scan(@NonNull BiFunction accumulator) { + Objects.requireNonNull(accumulator, "accumulator is null"); + return RxJavaPlugins.onAssembly(new ObservableScan<>(this, accumulator)); } /** - * Returns an Observable that applies a specified accumulator function to the first item emitted by a source - * ObservableSource and a seed value, then feeds the result of that function along with the second item emitted by - * the source ObservableSource into the same function, and so on until all items have been emitted by the source - * ObservableSource, emitting the result of each of these iterations. + * Returns an {@code Observable} that emits the provided initial (seed) value, then emits one value for each value emitted + * by the current {@code Observable}. Each emission after the first is the result of applying the specified accumulator + * function to the previous emission and the corresponding value from the current {@code Observable}. *

- * + * *

* This sort of function is sometimes called an accumulator. *

- * Note that the ObservableSource that results from this method will emit {@code initialValue} as its first + * Note that the {@code Observable} that results from this method will emit {@code initialValue} as its first * emitted item. *

- * Note that the {@code initialValue} is shared among all subscribers to the resulting ObservableSource + * Note that the {@code initialValue} is shared among all subscribers to the resulting {@code Observable} * and may cause problems if it is mutable. To make sure each subscriber gets its own value, defer * the application of this operator via {@link #defer(Supplier)}: *


@@ -11484,31 +12380,31 @@ public final Observable scan(BiFunction accumulator) {
      * @param initialValue
      *            the initial (seed) accumulator item
      * @param accumulator
-     *            an accumulator function to be invoked on each item emitted by the source ObservableSource, whose
+     *            an accumulator function to be invoked on each item emitted by the current {@code Observable}, whose
      *            result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the
      *            next accumulator call
-     * @return an Observable that emits {@code initialValue} followed by the results of each call to the
-     *         accumulator function
+     * @return the new {@code Observable} instance
+     * @throws NullPointerException if {@code initialValue} or {@code accumulator} is {@code null}
      * @see ReactiveX operators documentation: Scan
      */
     @CheckReturnValue
     @SchedulerSupport(SchedulerSupport.NONE)
-    public final  Observable scan(final R initialValue, BiFunction accumulator) {
-        ObjectHelper.requireNonNull(initialValue, "initialValue is null");
+    @NonNull
+    public final <@NonNull R> Observable scan(@NonNull R initialValue, @NonNull BiFunction accumulator) {
+        Objects.requireNonNull(initialValue, "initialValue is null");
         return scanWith(Functions.justSupplier(initialValue), accumulator);
     }
 
     /**
-     * Returns an Observable that applies a specified accumulator function to the first item emitted by a source
-     * ObservableSource and a seed value, then feeds the result of that function along with the second item emitted by
-     * the source ObservableSource into the same function, and so on until all items have been emitted by the source
-     * ObservableSource, emitting the result of each of these iterations.
+     * Returns an {@code Observable} that emits the provided initial (seed) value, then emits one value for each value emitted
+     * by the current {@code Observable}. Each emission after the first is the result of applying the specified accumulator
+     * function to the previous emission and the corresponding value from the current {@code Observable}.
      * 

- * + * *

* This sort of function is sometimes called an accumulator. *

- * Note that the ObservableSource that results from this method will emit the value returned + * Note that the {@code Observable} that results from this method will emit the value returned * by the {@code seedSupplier} as its first item. *

*
Scheduler:
@@ -11517,140 +12413,146 @@ public final Observable scan(final R initialValue, BiFunction the initial, accumulator and result type * @param seedSupplier - * a Supplier that returns the initial (seed) accumulator item for each individual Observer + * a {@link Supplier} that returns the initial (seed) accumulator item for each individual {@link Observer} * @param accumulator - * an accumulator function to be invoked on each item emitted by the source ObservableSource, whose - * result will be emitted to {@link Observer}s via {@link Observer#onNext onNext} and used in the + * an accumulator function to be invoked on each item emitted by the current {@code Observable}, whose + * result will be emitted to {@code Observer}s via {@link Observer#onNext onNext} and used in the * next accumulator call - * @return an Observable that emits {@code initialValue} followed by the results of each call to the - * accumulator function + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code seedSupplier} or {@code accumulator} is {@code null} * @see ReactiveX operators documentation: Scan */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable scanWith(Supplier seedSupplier, BiFunction accumulator) { - ObjectHelper.requireNonNull(seedSupplier, "seedSupplier is null"); - ObjectHelper.requireNonNull(accumulator, "accumulator is null"); - return RxJavaPlugins.onAssembly(new ObservableScanSeed(this, seedSupplier, accumulator)); + @NonNull + public final <@NonNull R> Observable scanWith(@NonNull Supplier seedSupplier, @NonNull BiFunction accumulator) { + Objects.requireNonNull(seedSupplier, "seedSupplier is null"); + Objects.requireNonNull(accumulator, "accumulator is null"); + return RxJavaPlugins.onAssembly(new ObservableScanSeed<>(this, seedSupplier, accumulator)); } /** - * Forces an ObservableSource's emissions and notifications to be serialized and for it to obey - * the ObservableSource contract in other ways. + * Forces the current {@code Observable}'s emissions and notifications to be serialized and for it to obey + * the {@code ObservableSource} contract in other ways. *

- * It is possible for an ObservableSource to invoke its Observers' methods asynchronously, perhaps from - * different threads. This could make such an ObservableSource poorly-behaved, in that it might try to invoke + * It is possible for an {@code Observable} to invoke its {@link Observer}s' methods asynchronously, perhaps from + * different threads. This could make such an {@code Observable} poorly-behaved, in that it might try to invoke * {@code onComplete} or {@code onError} before one of its {@code onNext} invocations, or it might call - * {@code onNext} from two different threads concurrently. You can force such an ObservableSource to be + * {@code onNext} from two different threads concurrently. You can force such an {@code Observable} to be * well-behaved and sequential by applying the {@code serialize} method to it. *

- * + * *

*
Scheduler:
*
{@code serialize} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@link ObservableSource} that is guaranteed to be well-behaved and to make only serialized calls to - * its observers + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Serialize */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable serialize() { - return RxJavaPlugins.onAssembly(new ObservableSerialized(this)); + return RxJavaPlugins.onAssembly(new ObservableSerialized<>(this)); } /** - * Returns a new {@link ObservableSource} that multicasts (and shares a single subscription to) the original {@link ObservableSource}. As long as - * there is at least one {@link Observer} this {@link ObservableSource} will be subscribed and emitting data. - * When all subscribers have disposed it will dispose the source {@link ObservableSource}. + * Returns a new {@code Observable} that multicasts (and shares a single subscription to) the current {@code Observable}. As long as + * there is at least one {@link Observer}, the current {@code Observable} will stay subscribed and keep emitting signals. + * When all observers have disposed, the operator will dispose the subscription to the current {@code Observable}. *

* This is an alias for {@link #publish()}.{@link ConnectableObservable#refCount() refCount()}. *

- * + * *

*
Scheduler:
*
{@code share} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@code ObservableSource} that upon connection causes the source {@code ObservableSource} to emit items - * to its {@link Observer}s + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: RefCount */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable share() { return publish().refCount(); } /** - * Returns a Maybe that completes if this Observable is empty or emits the single item emitted by this Observable, - * or signals an {@code IllegalArgumentException} if this Observable emits more than one item. + * Returns a {@link Maybe} that completes if the current {@code Observable} is empty or emits the single item + * emitted by the current {@code Observable}, or signals an {@link IllegalArgumentException} if the current + * {@code Observable} emits more than one item. *

- * + * *

*
Scheduler:
*
{@code singleElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Maybe} that emits the single item emitted by the source ObservableSource + * @return the new {@code Maybe} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Maybe singleElement() { - return RxJavaPlugins.onAssembly(new ObservableSingleMaybe(this)); + return RxJavaPlugins.onAssembly(new ObservableSingleMaybe<>(this)); } /** - * Returns a Single that emits the single item emitted by this Observable, if this Observable - * emits only a single item, or a default item if the source ObservableSource emits no items. If the source - * ObservableSource emits more than one item, an {@code IllegalArgumentException} is signalled instead. + * Returns a {@link Single} that emits the single item emitted by the current {@code Observable}, if the current {@code Observable} + * emits only a single item, or a default item if the current {@code Observable} emits no items. If the current + * {@code Observable} emits more than one item, an {@link IllegalArgumentException} is signaled instead. *

- * + * *

*
Scheduler:
*
{@code single} does not operate by default on a particular {@link Scheduler}.
*
* * @param defaultItem - * a default value to emit if the source ObservableSource emits no item - * @return the new Single instance + * a default value to emit if the current {@code Observable} emits no item + * @return the new {@code Single} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} * @see ReactiveX operators documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single single(T defaultItem) { - ObjectHelper.requireNonNull(defaultItem, "defaultItem is null"); - return RxJavaPlugins.onAssembly(new ObservableSingleSingle(this, defaultItem)); + @NonNull + public final Single single(@NonNull T defaultItem) { + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new ObservableSingleSingle<>(this, defaultItem)); } /** - * Returns a Single that emits the single item emitted by this Observable if this Observable + * Returns a {@link Single} that emits the single item emitted by the current {@code Observable} if it * emits only a single item, otherwise - * if this Observable completes without emitting any items or emits more than one item a - * {@link NoSuchElementException} or {@code IllegalArgumentException} will be signalled respectively. + * if the current {@code Observable} completes without emitting any items or emits more than one item a + * {@link NoSuchElementException} or {@link IllegalArgumentException} will be signaled respectively. *

- * + * *

*
Scheduler:
*
{@code singleOrError} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Single instance + * @return the new {@code Single} instance * @see ReactiveX operators documentation: First */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single singleOrError() { - return RxJavaPlugins.onAssembly(new ObservableSingleSingle(this, null)); + return RxJavaPlugins.onAssembly(new ObservableSingleSingle<>(this, null)); } /** - * Returns an Observable that skips the first {@code count} items emitted by the source ObservableSource and emits + * Returns an {@code Observable} that skips the first {@code count} items emitted by the current {@code Observable} and emits * the remainder. *

- * + * *

*
Scheduler:
*
This version of {@code skip} does not operate by default on a particular {@link Scheduler}.
@@ -11658,24 +12560,28 @@ public final Single singleOrError() { * * @param count * the number of items to skip - * @return an Observable that is identical to the source ObservableSource except that it does not emit the first - * {@code count} items that the source ObservableSource emits + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code count} is negative * @see ReactiveX operators documentation: Skip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable skip(long count) { - if (count <= 0) { + if (count < 0) { + throw new IllegalArgumentException("count >= 0 expected but it was " + count); + } + if (count == 0) { return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new ObservableSkip(this, count)); + return RxJavaPlugins.onAssembly(new ObservableSkip<>(this, count)); } /** - * Returns an Observable that skips values emitted by the source ObservableSource before a specified time window + * Returns an {@code Observable} that skips values emitted by the current {@code Observable} before a specified time window * elapses. *

- * + * *

*
Scheduler:
*
{@code skip} does not operate on any particular scheduler but uses the current time @@ -11686,24 +12592,25 @@ public final Observable skip(long count) { * the length of the time window to skip * @param unit * the time unit of {@code time} - * @return an Observable that skips values emitted by the source ObservableSource before the time window defined - * by {@code time} elapses and the emits the remainder + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Skip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable skip(long time, TimeUnit unit) { + @NonNull + public final Observable skip(long time, @NonNull TimeUnit unit) { return skipUntil(timer(time, unit)); } /** - * Returns an Observable that skips values emitted by the source ObservableSource before a specified time window + * Returns an {@code Observable} that skips values emitted by the current {@code Observable} before a specified time window * on a specified {@link Scheduler} elapses. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use for the timed skipping
+ *
You specify which {@code Scheduler} this operator will use for the timed skipping
*
* * @param time @@ -11711,25 +12618,26 @@ public final Observable skip(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the {@link Scheduler} on which the timed wait happens - * @return an Observable that skips values emitted by the source ObservableSource before the time window defined - * by {@code time} and {@code scheduler} elapses, and then emits the remainder + * the {@code Scheduler} on which the timed wait happens + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Skip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable skip(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable skip(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return skipUntil(timer(time, unit, scheduler)); } /** - * Returns an Observable that drops a specified number of items from the end of the sequence emitted by the - * source ObservableSource. + * Returns an {@code Observable} that drops a specified number of items from the end of the sequence emitted by the + * current {@code Observable}. *

- * + * *

- * This Observer accumulates a queue long enough to store the first {@code count} items. As more items are - * received, items are taken from the front of the queue and emitted by the returned ObservableSource. This causes + * This {@link Observer} accumulates a queue long enough to store the first {@code count} items. As more items are + * received, items are taken from the front of the queue and emitted by the returned {@code Observable}. This causes * such items to be delayed. *

*
Scheduler:
@@ -11738,56 +12646,57 @@ public final Observable skip(long time, TimeUnit unit, Scheduler scheduler) { * * @param count * number of items to drop from the end of the source sequence - * @return an Observable that emits the items emitted by the source ObservableSource except for the dropped ones - * at the end - * @throws IndexOutOfBoundsException - * if {@code count} is less than zero + * @return the new {@code Observable} instance + * @throws IllegalArgumentException + * if {@code count} is negative * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable skipLast(int count) { if (count < 0) { - throw new IndexOutOfBoundsException("count >= 0 required but it was " + count); + throw new IllegalArgumentException("count >= 0 required but it was " + count); } if (count == 0) { return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new ObservableSkipLast(this, count)); + return RxJavaPlugins.onAssembly(new ObservableSkipLast<>(this, count)); } /** - * Returns an Observable that drops items emitted by the source ObservableSource during a specified time window + * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

*
Scheduler:
*
{@code skipLast} does not operate on any particular scheduler but uses the current time - * from the {@code computation} {@link Scheduler}.
+ * from the {@code trampoline} {@link Scheduler}.
*
* * @param time * the length of the time window * @param unit * the time unit of {@code time} - * @return an Observable that drops those items emitted by the source ObservableSource in a time window before the - * source completes defined by {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.TRAMPOLINE) - public final Observable skipLast(long time, TimeUnit unit) { + @NonNull + public final Observable skipLast(long time, @NonNull TimeUnit unit) { return skipLast(time, unit, Schedulers.trampoline(), false, bufferSize()); } /** - * Returns an Observable that drops items emitted by the source ObservableSource during a specified time window + * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -11801,23 +12710,24 @@ public final Observable skipLast(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped - * @return an Observable that drops those items emitted by the source ObservableSource in a time window before the - * source completes defined by {@code time} + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.TRAMPOLINE) - public final Observable skipLast(long time, TimeUnit unit, boolean delayError) { + @NonNull + public final Observable skipLast(long time, @NonNull TimeUnit unit, boolean delayError) { return skipLast(time, unit, Schedulers.trampoline(), delayError, bufferSize()); } /** - * Returns an Observable that drops items emitted by the source ObservableSource during a specified time window + * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -11831,21 +12741,22 @@ public final Observable skipLast(long time, TimeUnit unit, boolean delayError * the time unit of {@code time} * @param scheduler * the scheduler used as the time source - * @return an Observable that drops those items emitted by the source ObservableSource in a time window before the - * source completes defined by {@code time} and {@code scheduler} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable skipLast(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable skipLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return skipLast(time, unit, scheduler, false, bufferSize()); } /** - * Returns an Observable that drops items emitted by the source ObservableSource during a specified time window + * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -11860,23 +12771,24 @@ public final Observable skipLast(long time, TimeUnit unit, Scheduler schedule * @param scheduler * the scheduler used as the time source * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped - * @return an Observable that drops those items emitted by the source ObservableSource in a time window before the - * source completes defined by {@code time} and {@code scheduler} + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable skipLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError) { + @NonNull + public final Observable skipLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { return skipLast(time, unit, scheduler, delayError, bufferSize()); } /** - * Returns an Observable that drops items emitted by the source ObservableSource during a specified time window + * Returns an {@code Observable} that drops items emitted by the current {@code Observable} during a specified time window * (defined on a specified scheduler) before the source completes. *

- * + * *

* Note: this action will cache the latest items arriving in the specified time window. *

@@ -11891,82 +12803,86 @@ public final Observable skipLast(long time, TimeUnit unit, Scheduler schedule * @param scheduler * the scheduler used as the time source * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped * @param bufferSize * the hint about how many elements to expect to be skipped - * @return an Observable that drops those items emitted by the source ObservableSource in a time window before the - * source completes defined by {@code time} and {@code scheduler} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: SkipLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable skipLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final Observable skipLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError, int bufferSize) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); // the internal buffer holds pairs of (timestamp, value) so double the default buffer size int s = bufferSize << 1; - return RxJavaPlugins.onAssembly(new ObservableSkipLastTimed(this, time, unit, scheduler, s, delayError)); + return RxJavaPlugins.onAssembly(new ObservableSkipLastTimed<>(this, time, unit, scheduler, s, delayError)); } /** - * Returns an Observable that skips items emitted by the source ObservableSource until a second ObservableSource emits + * Returns an {@code Observable} that skips items emitted by the current {@code Observable} until a second {@link ObservableSource} emits * an item. *

- * + * *

*
Scheduler:
*
{@code skipUntil} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the other ObservableSource + * @param the element type of the other {@code ObservableSource} * @param other - * the second ObservableSource that has to emit an item before the source ObservableSource's elements begin - * to be mirrored by the resulting ObservableSource - * @return an Observable that skips items from the source ObservableSource until the second ObservableSource emits an - * item, then emits the remaining items + * the second {@code ObservableSource} that has to emit an item before the current {@code Observable}'s elements begin + * to be mirrored by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: SkipUntil */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable skipUntil(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableSkipUntil(this, other)); + @NonNull + public final <@NonNull U> Observable skipUntil(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableSkipUntil<>(this, other)); } /** - * Returns an Observable that skips all items emitted by the source ObservableSource as long as a specified - * condition holds true, but emits all further source items as soon as the condition becomes false. + * Returns an {@code Observable} that skips all items emitted by the current {@code Observable} as long as a specified + * condition holds {@code true}, but emits all further source items as soon as the condition becomes {@code false}. *

- * + * *

*
Scheduler:
*
{@code skipWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function to test each item emitted from the source ObservableSource - * @return an Observable that begins emitting items emitted by the source ObservableSource when the specified - * predicate becomes false + * a function to test each item emitted from the current {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: SkipWhile */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable skipWhile(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableSkipWhile(this, predicate)); + @NonNull + public final Observable skipWhile(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new ObservableSkipWhile<>(this, predicate)); } /** - * Returns an Observable that emits the events emitted by source ObservableSource, in a - * sorted order. Each item emitted by the ObservableSource must implement {@link Comparable} with respect to all + * Returns an {@code Observable} that emits the events emitted by the current {@code Observable}, in a + * sorted order. Each item emitted by the current {@code Observable} must implement {@link Comparable} with respect to all * other items in the sequence. *

* *

- * If any item emitted by this Observable does not implement {@link Comparable} with respect to - * all other items emitted by this Observable, no items will be emitted and the + * If any item emitted by the current {@code Observable} does not implement {@code Comparable} with respect to + * all other items emitted by the current {@code Observable}, no items will be emitted and the * sequence is terminated with a {@link ClassCastException}. * *

Note that calling {@code sorted} with long, non-terminating or infinite sources @@ -11976,16 +12892,17 @@ public final Observable skipWhile(Predicate predicate) { *

Scheduler:
*
{@code sorted} does not operate by default on a particular {@link Scheduler}.
*
- * @return an Observable that emits the items emitted by the source ObservableSource in sorted order + * @return the new {@code Observable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable sorted() { - return toList().toObservable().map(Functions.listSorter(Functions.naturalComparator())).flatMapIterable(Functions.>identity()); + return toList().toObservable().map(Functions.listSorter(Functions.naturalComparator())).flatMapIterable(Functions.identity()); } /** - * Returns an Observable that emits the events emitted by source ObservableSource, in a + * Returns an {@code Observable} that emits the events emitted by the current {@code Observable}, in a * sorted order based on a specified comparison function. * *

Note that calling {@code sorted} with long, non-terminating or infinite sources @@ -11996,71 +12913,139 @@ public final Observable sorted() { *

{@code sorted} does not operate by default on a particular {@link Scheduler}.
*
* - * @param sortFunction - * a function that compares two items emitted by the source ObservableSource and returns an Integer + * @param comparator + * a function that compares two items emitted by the current {@code Observable} and returns an {@code int} * that indicates their sort order - * @return an Observable that emits the items emitted by the source ObservableSource in sorted order + * @throws NullPointerException if {@code comparator} is {@code null} + * @return the new {@code Observable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable sorted(Comparator sortFunction) { - ObjectHelper.requireNonNull(sortFunction, "sortFunction is null"); - return toList().toObservable().map(Functions.listSorter(sortFunction)).flatMapIterable(Functions.>identity()); + @NonNull + public final Observable sorted(@NonNull Comparator comparator) { + Objects.requireNonNull(comparator, "comparator is null"); + return toList().toObservable().map(Functions.listSorter(comparator)).flatMapIterable(Functions.identity()); } /** - * Returns an Observable that emits the items in a specified {@link Iterable} before it begins to emit items - * emitted by the source ObservableSource. + * Returns an {@code Observable} that emits the items in a specified {@link Iterable} before it begins to emit items + * emitted by the current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code startWithIterable} does not operate by default on a particular {@link Scheduler}.
*
* * @param items - * an Iterable that contains the items you want the modified ObservableSource to emit first - * @return an Observable that emits the items in the specified {@link Iterable} and then emits the items - * emitted by the source ObservableSource + * an {@code Iterable} that contains the items you want the resulting {@code Observable} to emit first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code items} is {@code null} * @see ReactiveX operators documentation: StartWith * @since 3.0.0 * @see #startWithItem(Object) * @see #startWithArray(Object...) */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable startWithIterable(Iterable items) { + @NonNull + public final Observable startWithIterable(@NonNull Iterable items) { return concatArray(fromIterable(items), this); } /** - * Returns an Observable that emits the items in a specified {@link ObservableSource} before it begins to emit - * items emitted by the source ObservableSource. + * Returns an {@code Observable} which first runs the other {@link CompletableSource} + * then the current {@code Observable} if the other completed normally. + *

+ * + *

+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code CompletableSource} to run first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Observable startWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); + return Observable.concat(Completable.wrap(other).toObservable(), this); + } + + /** + * Returns an {@code Observable} which first runs the other {@link SingleSource} + * then the current {@code Observable} if the other succeeded normally. + *

+ * + *

+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code SingleSource} to run first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Observable startWith(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); + return Observable.concat(Single.wrap(other).toObservable(), this); + } + + /** + * Returns an {@code Observable} which first runs the other {@link MaybeSource} + * then the current {@code Observable} if the other succeeded or completed normally. + *

+ * + *

+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code MaybeSource} to run first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Observable startWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return Observable.concat(Maybe.wrap(other).toObservable(), this); + } + + /** + * Returns an {@code Observable} that emits the items in a specified {@link ObservableSource} before it begins to emit + * items emitted by the current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code startWith} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * an ObservableSource that contains the items you want the modified ObservableSource to emit first - * @return an Observable that emits the items in the specified {@link ObservableSource} and then emits the items - * emitted by the source ObservableSource + * an {@code ObservableSource} that contains the items you want the modified {@code ObservableSource} to emit first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: StartWith */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable startWith(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + @NonNull + public final Observable startWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); return concatArray(other, this); } /** - * Returns an Observable that emits a specified item before it begins to emit items emitted by the source - * ObservableSource. + * Returns an {@code Observable} that emits a specified item before it begins to emit items emitted by the current + * {@code Observable}. *

* *

@@ -12070,26 +13055,25 @@ public final Observable startWith(ObservableSource other) { * * @param item * the item to emit first - * @return an Observable that emits the specified item before it begins to emit items emitted by the source - * ObservableSource + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: StartWith * @see #startWithArray(Object...) * @see #startWithIterable(Iterable) * @since 3.0.0 */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable startWithItem(T item) { - ObjectHelper.requireNonNull(item, "item is null"); + @NonNull + public final Observable startWithItem(@NonNull T item) { return concatArray(just(item), this); } /** - * Returns an Observable that emits the specified items before it begins to emit items emitted by the source - * ObservableSource. + * Returns an {@code Observable} that emits the specified items before it begins to emit items emitted by the current + * {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code startWithArray} does not operate by default on a particular {@link Scheduler}.
@@ -12097,16 +13081,17 @@ public final Observable startWithItem(T item) { * * @param items * the array of values to emit first - * @return an Observable that emits the specified items before it begins to emit items emitted by the source - * ObservableSource + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code items} is {@code null} * @see ReactiveX operators documentation: StartWith * @see #startWithItem(Object) * @see #startWithIterable(Iterable) */ - @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable startWithArray(T... items) { + @SafeVarargs + @NonNull + public final Observable startWithArray(@NonNull T... items) { Observable fromArray = fromArray(items); if (fromArray == empty()) { return RxJavaPlugins.onAssembly(this); @@ -12115,123 +13100,165 @@ public final Observable startWithArray(T... items) { } /** - * Subscribes to an ObservableSource and ignores {@code onNext} and {@code onComplete} emissions. + * Subscribes to the current {@code Observable} and ignores {@code onNext} and {@code onComplete} emissions. *

- * If the Observable emits an error, it is wrapped into an - * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * If the {@code Observable} emits an error, it is wrapped into an + * {@link OnErrorNotImplementedException} + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the ObservableSource has finished sending them + * @return the new {@link Disposable} instance that can be used to dispose the subscription at any time * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Disposable subscribe() { return subscribe(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to an ObservableSource and provides a callback to handle the items it emits. + * Subscribes to the current {@code Observable} and provides a callback to handle the items it emits. *

- * If the Observable emits an error, it is wrapped into an - * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * If the {@code Observable} emits an error, it is wrapped into an + * {@link OnErrorNotImplementedException} + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the {@code Consumer} you have designed to accept emissions from the ObservableSource - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the ObservableSource has finished sending them + * the {@code Consumer} you have designed to accept emissions from the current {@code Observable} + * @return the new {@link Disposable} instance that can be used to dispose the subscription at any time * @throws NullPointerException - * if {@code onNext} is null + * if {@code onNext} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onNext) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onNext) { return subscribe(onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); } /** - * Subscribes to an ObservableSource and provides callbacks to handle the items it emits and any error - * notification it issues. + * Subscribes to the current {@code Observable} and provides callbacks to handle the items it emits and any error + * notification it signals. *
*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the {@code Consumer} you have designed to accept emissions from the ObservableSource + * the {@code Consumer} you have designed to accept emissions from the current {@code Observable} * @param onError - * the {@code Consumer} you have designed to accept any error notification from the - * ObservableSource - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the ObservableSource has finished sending them - * @see ReactiveX operators documentation: Subscribe + * the {@code Consumer} you have designed to accept any error notification from the current + * {@code Observable} + * @return the new {@link Disposable} instance that can be used to dispose the subscription at any time * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null + * if {@code onNext} or {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onNext, Consumer onError) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onNext, @NonNull Consumer onError) { return subscribe(onNext, onError, Functions.EMPTY_ACTION); } /** - * Subscribes to an ObservableSource and provides callbacks to handle the items it emits and any error or - * completion notification it issues. + * Subscribes to the current {@code Observable} and provides callbacks to handle the items it emits and any error or + * completion notification it signals. *
*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onNext - * the {@code Consumer} you have designed to accept emissions from the ObservableSource + * the {@code Consumer} you have designed to accept emissions from the current {@code Observable} * @param onError - * the {@code Consumer} you have designed to accept any error notification from the - * ObservableSource + * the {@code Consumer} you have designed to accept any error notification from the current + * {@code Observable} * @param onComplete - * the {@code Action} you have designed to accept a completion notification from the - * ObservableSource - * @return a {@link Disposable} reference with which the caller can stop receiving items before - * the ObservableSource has finished sending them + * the {@link Action} you have designed to accept a completion notification from the current + * {@code Observable} + * @return the new {@link Disposable} instance that can be used to dispose the subscription at any time * @throws NullPointerException - * if {@code onNext} is null, or - * if {@code onError} is null, or - * if {@code onComplete} is null + * if {@code onNext}, {@code onError} or {@code onComplete} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, Action, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onNext, Consumer onError, - Action onComplete) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + @NonNull + public final Disposable subscribe(@NonNull Consumer onNext, @NonNull Consumer onError, + @NonNull Action onComplete) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); - LambdaObserver ls = new LambdaObserver(onNext, onError, onComplete, Functions.emptyConsumer()); + LambdaObserver ls = new LambdaObserver<>(onNext, onError, onComplete, Functions.emptyConsumer()); subscribe(ls); return ls; } + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link Observer}, + * adds it to the given {@code DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code Observer} is removed + * from the given container. + *

+ * The {@code Observer} will be removed after the callback for the terminal event has been invoked. + *

+ *
Scheduler:
+ *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param onNext the callback for upstream items + * @param onError the callback for an upstream error if any + * @param onComplete the callback for the upstream completion if any + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code Observer} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onNext}, {@code onError}, + * {@code onComplete} or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onNext, + @NonNull Consumer onError, + @NonNull Action onComplete, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseObserver observer = new DisposableAutoReleaseObserver<>( + container, onNext, onError, onComplete); + container.add(observer); + subscribe(observer); + return observer; + } + @SchedulerSupport(SchedulerSupport.NONE) @Override - public final void subscribe(Observer observer) { - ObjectHelper.requireNonNull(observer, "observer is null"); + public final void subscribe(@NonNull Observer observer) { + Objects.requireNonNull(observer, "observer is null"); try { observer = RxJavaPlugins.onSubscribe(this, observer); - ObjectHelper.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null Observer. Please change the handler provided to RxJavaPlugins.setOnObservableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); + Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null Observer. Please change the handler provided to RxJavaPlugins.setOnObservableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); subscribeActual(observer); } catch (NullPointerException e) { // NOPMD @@ -12254,13 +13281,13 @@ public final void subscribe(Observer observer) { *

There is no need to call any of the plugin hooks on the current {@code Observable} instance or * the {@code Observer}; all hooks and basic safeguards have been * applied by {@link #subscribe(Observer)} before this method gets called. - * @param observer the incoming Observer, never null + * @param observer the incoming {@code Observer}, never {@code null} */ - protected abstract void subscribeActual(Observer observer); + protected abstract void subscribeActual(@NonNull Observer observer); /** - * Subscribes a given Observer (subclass) to this Observable and returns the given - * Observer as is. + * Subscribes a given {@link Observer} (subclass) to the current {@code Observable} and returns the given + * {@code Observer} instance as is. *

Usage example: *


      * Observable<Integer> source = Observable.range(1, 10);
@@ -12276,122 +13303,130 @@ public final void subscribe(Observer observer) {
      *  
Scheduler:
*
{@code subscribeWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param the type of the Observer to use and return - * @param observer the Observer (subclass) to use and return, not null + * @param the type of the {@code Observer} to use and return + * @param observer the {@code Observer} (subclass) to use and return, not {@code null} * @return the input {@code observer} - * @throws NullPointerException if {@code observer} is null + * @throws NullPointerException if {@code observer} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > E subscribeWith(E observer) { + @NonNull + public final <@NonNull E extends Observer> E subscribeWith(E observer) { subscribe(observer); return observer; } /** - * Asynchronously subscribes Observers to this ObservableSource on the specified {@link Scheduler}. + * Asynchronously subscribes {@link Observer}s to the current {@code Observable} on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to perform subscription actions on - * @return the source ObservableSource modified so that its subscriptions happen on the - * specified {@link Scheduler} + * the {@code Scheduler} to perform subscription actions on + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn * @see RxJava Threading Examples * @see #observeOn */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable subscribeOn(Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler)); + @NonNull + public final Observable subscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<>(this, scheduler)); } /** - * Returns an Observable that emits the items emitted by the source ObservableSource or the items of an alternate - * ObservableSource if the source ObservableSource is empty. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} or the items of an alternate + * {@link ObservableSource} if the current {@code Observable} is empty. *

- * + * *

*
Scheduler:
*
{@code switchIfEmpty} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * the alternate ObservableSource to subscribe to if the source does not emit any items - * @return an ObservableSource that emits the items emitted by the source ObservableSource or the items of an - * alternate ObservableSource if the source ObservableSource is empty. + * the alternate {@code ObservableSource} to subscribe to if the source does not emit any items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @since 1.1.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable switchIfEmpty(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchIfEmpty(this, other)); + @NonNull + public final Observable switchIfEmpty(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchIfEmpty<>(this, other)); } /** - * Returns a new ObservableSource by applying a function that you supply to each item emitted by the source - * ObservableSource that returns an ObservableSource, and then emitting the items emitted by the most recently emitted - * of these ObservableSources. + * Returns a new {@code Observable} by applying a function that you supply to each item emitted by the current + * {@code Observable} that returns an {@link ObservableSource}, and then emitting the items emitted by the most recently emitted + * of these {@code ObservableSource}s. *

- * The resulting ObservableSource completes if both the upstream ObservableSource and the last inner ObservableSource, if any, complete. - * If the upstream ObservableSource signals an onError, the inner ObservableSource is disposed and the error delivered in-sequence. + * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code ObservableSource}, if any, complete. + * If the current {@code Observable} signals an {@code onError}, the inner {@code ObservableSource} is disposed and the error delivered in-sequence. *

- * + * *

*
Scheduler:
*
{@code switchMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner ObservableSources and the output + * @param the element type of the inner {@code ObservableSource}s and the output * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource - * @return an Observable that emits the items emitted by the ObservableSource returned from applying {@code func} to the most recently emitted item emitted by the source ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #switchMapDelayError(Function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable switchMap(Function> mapper) { + @NonNull + public final <@NonNull R> Observable switchMap(@NonNull Function> mapper) { return switchMap(mapper, bufferSize()); } /** - * Returns a new ObservableSource by applying a function that you supply to each item emitted by the source - * ObservableSource that returns an ObservableSource, and then emitting the items emitted by the most recently emitted - * of these ObservableSources. + * Returns a new {@code Observable} by applying a function that you supply to each item emitted by the current + * {@code Observable} that returns an {@link ObservableSource}, and then emitting the items emitted by the most recently emitted + * of these {@code ObservableSource}s. *

- * The resulting ObservableSource completes if both the upstream ObservableSource and the last inner ObservableSource, if any, complete. - * If the upstream ObservableSource signals an onError, the inner ObservableSource is disposed and the error delivered in-sequence. + * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code ObservableSource}, if any, complete. + * If the current {@code Observable} signals an {@code onError}, the inner {@code ObservableSource} is disposed and the error delivered in-sequence. *

- * + * *

*
Scheduler:
*
{@code switchMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner ObservableSources and the output + * @param the element type of the inner {@code ObservableSource}s and the output * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} * @param bufferSize - * the number of elements to prefetch from the current active inner ObservableSource - * @return an Observable that emits the items emitted by the ObservableSource returned from applying {@code func} to the most recently emitted item emitted by the source ObservableSource + * the number of elements expected from the current active inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @see #switchMapDelayError(Function, int) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable switchMap(Function> mapper, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + @NonNull + public final <@NonNull R> Observable switchMap(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") @@ -12401,48 +13436,50 @@ public final Observable switchMap(Function(this, mapper, bufferSize, false)); + return RxJavaPlugins.onAssembly(new ObservableSwitchMap<>(this, mapper, bufferSize, false)); } /** - * Maps the upstream values into {@link CompletableSource}s, subscribes to the newer one while + * Maps the items of the current {@code Observable} into {@link CompletableSource}s, subscribes to the newer one while * disposing the subscription to the previous {@code CompletableSource}, thus keeping at most one * active {@code CompletableSource} running. *

- * + * *

* Since a {@code CompletableSource} doesn't produce any items, the resulting reactive type of * this operator is a {@link Completable} that can only indicate successful completion or * a failure in any of the inner {@code CompletableSource}s or the failure of the current - * {@link Observable}. + * {@code Observable}. *

*
Scheduler:
*
{@code switchMapCompletable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If either this {@code Observable} or the active {@code CompletableSource} signals an {@code onError}, - * the resulting {@code Completable} is terminated immediately with that {@code Throwable}. + *
If either the current {@code Observable} or the active {@code CompletableSource} signals an {@code onError}, + * the resulting {@code Completable} is terminated immediately with that {@link Throwable}. * Use the {@link #switchMapCompletableDelayError(Function)} to delay such inner failures until * every inner {@code CompletableSource}s and the main {@code Observable} terminates in some fashion. * If they fail concurrently, the operator may combine the {@code Throwable}s into a - * {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException} + * {@link CompositeException} * and signal it to the downstream instead. If any inactivated (switched out) {@code CompletableSource} - * signals an {@code onError} late, the {@code Throwable}s will be signalled to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. + * signals an {@code onError} late, the {@code Throwable}s will be signaled to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. *
*
*

History: 2.1.11 - experimental * @param mapper the function called with each upstream item and should return a - * {@link CompletableSource} to be subscribed to and awaited for + * {@code CompletableSource} to be subscribed to and awaited for * (non blockingly) for its terminal event - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see #switchMapCompletableDelayError(Function) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable switchMapCompletable(@NonNull Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMapCompletable(this, mapper, false)); + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMapCompletable<>(this, mapper, false)); } /** @@ -12456,79 +13493,83 @@ public final Completable switchMapCompletable(@NonNull Function *

Scheduler:
*
{@code switchMapCompletableDelayError} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
Errors of this {@code Observable} and all the {@code CompletableSource}s, who had the chance + *
The errors of the current {@code Observable} and all the {@code CompletableSource}s, who had the chance * to run to their completion, are delayed until * all of them terminate in some fashion. At this point, if there was only one failure, the respective - * {@code Throwable} is emitted to the downstream. It there were more than one failures, the - * operator combines all {@code Throwable}s into a {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException} + * {@link Throwable} is emitted to the downstream. It there were more than one failures, the + * operator combines all {@code Throwable}s into a {@link CompositeException} * and signals that to the downstream. * If any inactivated (switched out) {@code CompletableSource} - * signals an {@code onError} late, the {@code Throwable}s will be signalled to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. + * signals an {@code onError} late, the {@code Throwable}s will be signaled to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. *
*
*

History: 2.1.11 - experimental * @param mapper the function called with each upstream item and should return a - * {@link CompletableSource} to be subscribed to and awaited for + * {@code CompletableSource} to be subscribed to and awaited for * (non blockingly) for its terminal event - * @return the new Completable instance + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see #switchMapCompletable(Function) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable switchMapCompletableDelayError(@NonNull Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMapCompletable(this, mapper, true)); + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMapCompletable<>(this, mapper, true)); } /** - * Maps the upstream items into {@link MaybeSource}s and switches (subscribes) to the newer ones + * Maps the items of the current {@code Observable} into {@link MaybeSource}s and switches (subscribes) to the newer ones * while disposing the older ones (and ignoring their signals) and emits the latest success value of the current one if - * available while failing immediately if this {@code Observable} or any of the + * available while failing immediately if the current {@code Observable} or any of the * active inner {@code MaybeSource}s fail. *

- * + * *

*
Scheduler:
*
{@code switchMapMaybe} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
This operator terminates with an {@code onError} if this {@code Observable} or any of + *
This operator terminates with an {@code onError} if the current {@code Observable} or any of * the inner {@code MaybeSource}s fail while they are active. When this happens concurrently, their - * individual {@code Throwable} errors may get combined and emitted as a single - * {@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException}. Otherwise, a late - * (i.e., inactive or switched out) {@code onError} from this {@code Observable} or from any of + * individual {@link Throwable} errors may get combined and emitted as a single + * {@link CompositeException}. Otherwise, a late + * (i.e., inactive or switched out) {@code onError} from the current {@code Observable} or from any of * the inner {@code MaybeSource}s will be forwarded to the global error handler via - * {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable)} as - * {@link io.reactivex.rxjava3.exceptions.UndeliverableException UndeliverableException}
+ * {@link RxJavaPlugins#onError(Throwable)} as + * {@link UndeliverableException} *
*

History: 2.1.11 - experimental * @param the output value type * @param mapper the function called with the current upstream event and should * return a {@code MaybeSource} to replace the current active inner source * and get subscribed to. - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see #switchMapMaybeDelayError(Function) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable switchMapMaybe(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMapMaybe(this, mapper, false)); + @NonNull + public final <@NonNull R> Observable switchMapMaybe(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMapMaybe<>(this, mapper, false)); } /** * Maps the upstream items into {@link MaybeSource}s and switches (subscribes) to the newer ones * while disposing the older ones (and ignoring their signals) and emits the latest success value of the current one if - * available, delaying errors from this {@code Observable} or the inner {@code MaybeSource}s until all terminate. + * available, delaying errors from the current {@code Observable} or the inner {@code MaybeSource}s until all terminate. *

- * + * *

*
Scheduler:
*
{@code switchMapMaybeDelayError} does not operate by default on a particular {@link Scheduler}.
@@ -12538,36 +13579,39 @@ public final Observable switchMapMaybe(@NonNull Function Observable switchMapMaybeDelayError(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMapMaybe(this, mapper, true)); + @NonNull + public final <@NonNull R> Observable switchMapMaybeDelayError(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMapMaybe<>(this, mapper, true)); } /** - * Returns a new ObservableSource by applying a function that you supply to each item emitted by the source - * ObservableSource that returns a SingleSource, and then emitting the item emitted by the most recently emitted - * of these SingleSources. + * Returns a new {@code Observable} by applying a function that you supply to each item emitted by the current + * {@code Observable} that returns a {@link SingleSource}, and then emitting the item emitted by the most recently emitted + * of these {@code SingleSource}s. *

- * The resulting ObservableSource completes if both the upstream ObservableSource and the last inner SingleSource, if any, complete. - * If the upstream ObservableSource signals an onError, the inner SingleSource is disposed and the error delivered in-sequence. + * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code SingleSource}, if any, complete. + * If the current {@code Observable} signals an {@code onError}, the inner {@code SingleSource} is disposed and the error delivered in-sequence. *

- * + * *

*
Scheduler:
*
{@code switchMapSingle} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.8 - experimental - * @param the element type of the inner SingleSources and the output + * @param the element type of the inner {@code SingleSource}s and the output * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns a - * SingleSource - * @return an Observable that emits the item emitted by the SingleSource returned from applying {@code func} to the most recently emitted item emitted by the source ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns a + * {@code SingleSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #switchMapSingleDelayError(Function) * @since 2.2 @@ -12575,19 +13619,19 @@ public final Observable switchMapMaybeDelayError(@NonNull Function Observable switchMapSingle(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMapSingle(this, mapper, false)); + public final <@NonNull R> Observable switchMapSingle(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMapSingle<>(this, mapper, false)); } /** - * Returns a new ObservableSource by applying a function that you supply to each item emitted by the source - * ObservableSource that returns a SingleSource, and then emitting the item emitted by the most recently emitted - * of these SingleSources and delays any error until all SingleSources terminate. + * Returns a new {@code Observable} by applying a function that you supply to each item emitted by the current + * {@code Observable} that returns a {@link SingleSource}, and then emitting the item emitted by the most recently emitted + * of these {@code SingleSource}s and delays any error until all {@code SingleSource}s terminate. *

- * The resulting ObservableSource completes if both the upstream ObservableSource and the last inner SingleSource, if any, complete. - * If the upstream ObservableSource signals an onError, the termination of the last inner SingleSource will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner SingleSources signalled. + * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code SingleSource}, if any, complete. + * If the current {@code Observable} signals an {@code onError}, the termination of the last inner {@code SingleSource} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code SingleSource}s signaled. *

* *

@@ -12595,11 +13639,12 @@ public final Observable switchMapSingle(@NonNull Function{@code switchMapSingleDelayError} does not operate by default on a particular {@link Scheduler}. *
*

History: 2.0.8 - experimental - * @param the element type of the inner SingleSources and the output + * @param the element type of the inner {@code SingleSource}s and the output * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns a - * SingleSource - * @return an Observable that emits the item emitted by the SingleSource returned from applying {@code func} to the most recently emitted item emitted by the source ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns a + * {@code SingleSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #switchMapSingle(Function) * @since 2.2 @@ -12607,71 +13652,76 @@ public final Observable switchMapSingle(@NonNull Function Observable switchMapSingleDelayError(@NonNull Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new ObservableSwitchMapSingle(this, mapper, true)); + public final <@NonNull R> Observable switchMapSingleDelayError(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableSwitchMapSingle<>(this, mapper, true)); } /** - * Returns a new ObservableSource by applying a function that you supply to each item emitted by the source - * ObservableSource that returns an ObservableSource, and then emitting the items emitted by the most recently emitted - * of these ObservableSources and delays any error until all ObservableSources terminate. + * Returns a new {@code Observable} by applying a function that you supply to each item emitted by the current + * {@code Observable} that returns an {@link ObservableSource}, and then emitting the items emitted by the most recently emitted + * of these {@code ObservableSource}s and delays any error until all {@code ObservableSource}s terminate. *

- * The resulting ObservableSource completes if both the upstream ObservableSource and the last inner ObservableSource, if any, complete. - * If the upstream ObservableSource signals an onError, the termination of the last inner ObservableSource will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner ObservableSources signalled. + * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code ObservableSource}, if any, complete. + * If the current {@code Observable} signals an {@code onError}, the termination of the last inner {@code ObservableSource} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code ObservableSource}s signaled. *

- * + * *

*
Scheduler:
*
{@code switchMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner ObservableSources and the output + * @param the element type of the inner {@code ObservableSource}s and the output * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource - * @return an Observable that emits the items emitted by the ObservableSource returned from applying {@code func} to the most recently emitted item emitted by the source ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @see #switchMap(Function) * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable switchMapDelayError(Function> mapper) { + @NonNull + public final <@NonNull R> Observable switchMapDelayError(@NonNull Function> mapper) { return switchMapDelayError(mapper, bufferSize()); } /** - * Returns a new ObservableSource by applying a function that you supply to each item emitted by the source - * ObservableSource that returns an ObservableSource, and then emitting the items emitted by the most recently emitted - * of these ObservableSources and delays any error until all ObservableSources terminate. + * Returns a new {@code Observable} by applying a function that you supply to each item emitted by the current + * {@code Observable} that returns an {@link ObservableSource}, and then emitting the items emitted by the most recently emitted + * of these {@code ObservableSource}s and delays any error until all {@code ObservableSource}s terminate. *

- * The resulting ObservableSource completes if both the upstream ObservableSource and the last inner ObservableSource, if any, complete. - * If the upstream ObservableSource signals an onError, the termination of the last inner ObservableSource will emit that error as is - * or wrapped into a CompositeException along with the other possible errors the former inner ObservableSources signalled. + * The resulting {@code Observable} completes if both the current {@code Observable} and the last inner {@code ObservableSource}, if any, complete. + * If the current {@code Observable} signals an {@code onError}, the termination of the last inner {@code ObservableSource} will emit that error as is + * or wrapped into a {@link CompositeException} along with the other possible errors the former inner {@code ObservableSource}s signaled. *

- * + * *

*
Scheduler:
*
{@code switchMapDelayError} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the inner ObservableSources and the output + * @param the element type of the inner {@code ObservableSource}s and the output * @param mapper - * a function that, when applied to an item emitted by the source ObservableSource, returns an - * ObservableSource + * a function that, when applied to an item emitted by the current {@code Observable}, returns an + * {@code ObservableSource} * @param bufferSize - * the number of elements to prefetch from the current active inner ObservableSource - * @return an Observable that emits the items emitted by the ObservableSource returned from applying {@code func} to the most recently emitted item emitted by the source ObservableSource + * the number of elements expected from the current active inner {@code ObservableSource} to be buffered + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: FlatMap * @see #switchMap(Function, int) * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable switchMapDelayError(Function> mapper, int bufferSize) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + @NonNull + public final <@NonNull R> Observable switchMapDelayError(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (this instanceof ScalarSupplier) { @SuppressWarnings("unchecked") @@ -12681,18 +13731,22 @@ public final Observable switchMapDelayError(Function(this, mapper, bufferSize, true)); + return RxJavaPlugins.onAssembly(new ObservableSwitchMap<>(this, mapper, bufferSize, true)); } /** - * Returns an Observable that emits only the first {@code count} items emitted by the source ObservableSource. If the source emits fewer than - * {@code count} items then all of its items are emitted. + * Returns an {@code Observable} that emits only the first {@code count} items emitted by the current {@code Observable}. + * If the source emits fewer than {@code count} items then all of its items are emitted. *

- * + * *

- * This method returns an ObservableSource that will invoke a subscribing {@link Observer}'s + * This method returns an {@code Observable} that will invoke a subscribing {@link Observer}'s * {@link Observer#onNext onNext} function a maximum of {@code count} times before invoking * {@link Observer#onComplete onComplete}. + *

+ * Taking {@code 0} items from the current {@code Observable} will still subscribe to it, allowing the + * subscription-time side-effects to happen there, but will be immediately disposed and the downstream completed + * without any item emission. *

*
Scheduler:
*
This version of {@code take} does not operate by default on a particular {@link Scheduler}.
@@ -12700,56 +13754,59 @@ public final Observable switchMapDelayError(FunctionReactiveX operators documentation: Take */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable take(long count) { if (count < 0) { throw new IllegalArgumentException("count >= 0 required but it was " + count); } - return RxJavaPlugins.onAssembly(new ObservableTake(this, count)); + return RxJavaPlugins.onAssembly(new ObservableTake<>(this, count)); } /** - * Returns an Observable that emits those items emitted by source ObservableSource before a specified time runs + * Returns an {@code Observable} that emits those items emitted by the current {@code Observable} before a specified time runs * out. *

* If time runs out before the {@code Observable} completes normally, the {@code onComplete} event will be * signaled on the default {@code computation} {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
This version of {@code take} operates by default on the {@code computation} {@link Scheduler}.
+ *
This version of {@code take} operates by default on the {@code computation} {@code Scheduler}.
*
* * @param time * the length of the time window * @param unit * the time unit of {@code time} - * @return an Observable that emits those items emitted by the source ObservableSource before the time runs out + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Take */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable take(long time, TimeUnit unit) { + @NonNull + public final Observable take(long time, @NonNull TimeUnit unit) { return takeUntil(timer(time, unit)); } /** - * Returns an Observable that emits those items emitted by source ObservableSource before a specified time (on a - * specified Scheduler) runs out. + * Returns an {@code Observable} that emits those items emitted by the current {@code Observable} before a specified time (on a + * specified {@link Scheduler}) runs out. *

* If time runs out before the {@code Observable} completes normally, the {@code onComplete} event will be - * signaled on the provided {@link Scheduler}. + * signaled on the provided {@code Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -12757,59 +13814,61 @@ public final Observable take(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler used for time source - * @return an Observable that emits those items emitted by the source ObservableSource before the time runs out, - * according to the specified Scheduler + * the {@code Scheduler} used for time source + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Take */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable take(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable take(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return takeUntil(timer(time, unit, scheduler)); } /** - * Returns an Observable that emits at most the last {@code count} items emitted by the source ObservableSource. If the source emits fewer than - * {@code count} items then all of its items are emitted. + * Returns an {@code Observable} that emits at most the last {@code count} items emitted by the current {@code Observable}. + * If the source emits fewer than {@code count} items then all of its items are emitted. *

- * + * *

*
Scheduler:
*
This version of {@code takeLast} does not operate by default on a particular {@link Scheduler}.
*
* * @param count - * the maximum number of items to emit from the end of the sequence of items emitted by the source - * ObservableSource - * @return an Observable that emits at most the last {@code count} items emitted by the source ObservableSource - * @throws IndexOutOfBoundsException - * if {@code count} is less than zero + * the maximum number of items to emit from the end of the sequence of items emitted by the current + * {@code Observable} + * @return the new {@code Observable} instance + * @throws IllegalArgumentException + * if {@code count} is negative * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable takeLast(int count) { if (count < 0) { - throw new IndexOutOfBoundsException("count >= 0 required but it was " + count); + throw new IllegalArgumentException("count >= 0 required but it was " + count); } if (count == 0) { - return RxJavaPlugins.onAssembly(new ObservableIgnoreElements(this)); + return RxJavaPlugins.onAssembly(new ObservableIgnoreElements<>(this)); } if (count == 1) { - return RxJavaPlugins.onAssembly(new ObservableTakeLastOne(this)); + return RxJavaPlugins.onAssembly(new ObservableTakeLastOne<>(this)); } - return RxJavaPlugins.onAssembly(new ObservableTakeLast(this, count)); + return RxJavaPlugins.onAssembly(new ObservableTakeLast<>(this, count)); } /** - * Returns an Observable that emits at most a specified number of items from the source ObservableSource that were - * emitted in a specified window of time before the ObservableSource completed. + * Returns an {@code Observable} that emits at most a specified number of items from the current {@code Observable} that were + * emitted in a specified window of time before the current {@code Observable} completed. *

- * + * *

*
Scheduler:
*
{@code takeLast} does not operate on any particular scheduler but uses the current time - * from the {@code computation} {@link Scheduler}.
+ * from the {@code trampoline} {@link Scheduler}. *
* * @param count @@ -12818,25 +13877,27 @@ public final Observable takeLast(int count) { * the length of the time window * @param unit * the time unit of {@code time} - * @return an Observable that emits at most {@code count} items from the source ObservableSource that were emitted - * in a specified window of time before the ObservableSource completed + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is negative * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.TRAMPOLINE) - public final Observable takeLast(long count, long time, TimeUnit unit) { + @NonNull + public final Observable takeLast(long count, long time, @NonNull TimeUnit unit) { return takeLast(count, time, unit, Schedulers.trampoline(), false, bufferSize()); } /** - * Returns an Observable that emits at most a specified number of items from the source ObservableSource that were - * emitted in a specified window of time before the ObservableSource completed, where the timing information is - * provided by a given Scheduler. + * Returns an {@code Observable} that emits at most a specified number of items from the current {@code Observable} that were + * emitted in a specified window of time before the current {@code Observable} completed, where the timing information is + * provided by a given {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use for tracking the current time
+ *
You specify which {@code Scheduler} this operator will use for tracking the current time
*
* * @param count @@ -12846,29 +13907,29 @@ public final Observable takeLast(long count, long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param scheduler - * the {@link Scheduler} that provides the timestamps for the observed items - * @return an Observable that emits at most {@code count} items from the source ObservableSource that were emitted - * in a specified window of time before the ObservableSource completed, where the timing information is - * provided by the given {@code scheduler} - * @throws IndexOutOfBoundsException - * if {@code count} is less than zero + * the {@code Scheduler} that provides the timestamps for the observed items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is negative * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable takeLast(long count, long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable takeLast(long count, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return takeLast(count, time, unit, scheduler, false, bufferSize()); } /** - * Returns an Observable that emits at most a specified number of items from the source ObservableSource that were - * emitted in a specified window of time before the ObservableSource completed, where the timing information is - * provided by a given Scheduler. + * Returns an {@code Observable} that emits at most a specified number of items from the current {@code Observable} that were + * emitted in a specified window of time before the current {@code Observable} completed, where the timing information is + * provided by a given {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use for tracking the current time
+ *
You specify which {@code Scheduler} this operator will use for tracking the current time
*
* * @param count @@ -12878,63 +13939,66 @@ public final Observable takeLast(long count, long time, TimeUnit unit, Schedu * @param unit * the time unit of {@code time} * @param scheduler - * the {@link Scheduler} that provides the timestamps for the observed items + * the {@code Scheduler} that provides the timestamps for the observed items * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped * @param bufferSize * the hint about how many elements to expect to be last - * @return an Observable that emits at most {@code count} items from the source ObservableSource that were emitted - * in a specified window of time before the ObservableSource completed, where the timing information is - * provided by the given {@code scheduler} - * @throws IndexOutOfBoundsException - * if {@code count} is less than zero + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException + * if {@code count} is negative or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable takeLast(long count, long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + @NonNull + public final Observable takeLast(long count, long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError, int bufferSize) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); if (count < 0) { - throw new IndexOutOfBoundsException("count >= 0 required but it was " + count); + throw new IllegalArgumentException("count >= 0 required but it was " + count); } - return RxJavaPlugins.onAssembly(new ObservableTakeLastTimed(this, count, time, unit, scheduler, bufferSize, delayError)); + return RxJavaPlugins.onAssembly(new ObservableTakeLastTimed<>(this, count, time, unit, scheduler, bufferSize, delayError)); } /** - * Returns an Observable that emits the items from the source ObservableSource that were emitted in a specified - * window of time before the ObservableSource completed. + * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified + * window of time before the current {@code Observable} completed. *

- * + * *

*
Scheduler:
- *
This version of {@code takeLast} operates by default on the {@code computation} {@link Scheduler}.
+ *
{@code takeLast} does not operate on any particular scheduler but uses the current time + * from the {@code trampoline} {@link Scheduler}.
*
* * @param time * the length of the time window * @param unit * the time unit of {@code time} - * @return an Observable that emits the items from the source ObservableSource that were emitted in the window of - * time before the ObservableSource completed specified by {@code time} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.TRAMPOLINE) - public final Observable takeLast(long time, TimeUnit unit) { + @NonNull + public final Observable takeLast(long time, @NonNull TimeUnit unit) { return takeLast(time, unit, Schedulers.trampoline(), false, bufferSize()); } /** - * Returns an Observable that emits the items from the source ObservableSource that were emitted in a specified - * window of time before the ObservableSource completed. + * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified + * window of time before the current {@code Observable} completed. *

- * + * *

*
Scheduler:
- *
This version of {@code takeLast} operates by default on the {@code computation} {@link Scheduler}.
+ *
{@code takeLast} does not operate on any particular scheduler but uses the current time + * from the {@code trampoline} {@link Scheduler}.
*
* * @param time @@ -12942,27 +14006,29 @@ public final Observable takeLast(long time, TimeUnit unit) { * @param unit * the time unit of {@code time} * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped - * @return an Observable that emits the items from the source ObservableSource that were emitted in the window of - * time before the ObservableSource completed specified by {@code time} + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.TRAMPOLINE) - public final Observable takeLast(long time, TimeUnit unit, boolean delayError) { + @NonNull + public final Observable takeLast(long time, @NonNull TimeUnit unit, boolean delayError) { return takeLast(time, unit, Schedulers.trampoline(), delayError, bufferSize()); } /** - * Returns an Observable that emits the items from the source ObservableSource that were emitted in a specified - * window of time before the ObservableSource completed, where the timing information is provided by a specified - * Scheduler. + * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified + * window of time before the current {@code Observable} completed, where the timing information is provided by a specified + * {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -12970,27 +14036,27 @@ public final Observable takeLast(long time, TimeUnit unit, boolean delayError * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that provides the timestamps for the Observed items - * @return an Observable that emits the items from the source ObservableSource that were emitted in the window of - * time before the ObservableSource completed specified by {@code time}, where the timing information is - * provided by {@code scheduler} + * the {@code Scheduler} that provides the timestamps for the observed items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable takeLast(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable takeLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return takeLast(time, unit, scheduler, false, bufferSize()); } /** - * Returns an Observable that emits the items from the source ObservableSource that were emitted in a specified - * window of time before the ObservableSource completed, where the timing information is provided by a specified - * Scheduler. + * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified + * window of time before the current {@code Observable} completed, where the timing information is provided by a specified + * {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -12998,30 +14064,30 @@ public final Observable takeLast(long time, TimeUnit unit, Scheduler schedule * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that provides the timestamps for the Observed items + * the {@code Scheduler} that provides the timestamps for the observed items * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped - * @return an Observable that emits the items from the source ObservableSource that were emitted in the window of - * time before the ObservableSource completed specified by {@code time}, where the timing information is - * provided by {@code scheduler} + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable takeLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError) { + @NonNull + public final Observable takeLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { return takeLast(time, unit, scheduler, delayError, bufferSize()); } /** - * Returns an Observable that emits the items from the source ObservableSource that were emitted in a specified - * window of time before the ObservableSource completed, where the timing information is provided by a specified - * Scheduler. + * Returns an {@code Observable} that emits the items from the current {@code Observable} that were emitted in a specified + * window of time before the current {@code Observable} completed, where the timing information is provided by a specified + * {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param time @@ -13029,53 +14095,56 @@ public final Observable takeLast(long time, TimeUnit unit, Scheduler schedule * @param unit * the time unit of {@code time} * @param scheduler - * the Scheduler that provides the timestamps for the Observed items + * the {@code Scheduler} that provides the timestamps for the observed items * @param delayError - * if true, an exception signalled by the current Observable is delayed until the regular elements are consumed - * by the downstream; if false, an exception is immediately signalled and all regular elements dropped + * if {@code true}, an exception signaled by the current {@code Observable} is delayed until the regular elements are consumed + * by the downstream; if {@code false}, an exception is immediately signaled and all regular elements dropped * @param bufferSize * the hint about how many elements to expect to be last - * @return an Observable that emits the items from the source ObservableSource that were emitted in the window of - * time before the ObservableSource completed specified by {@code time}, where the timing information is - * provided by {@code scheduler} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: TakeLast */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable takeLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize) { + @NonNull + public final Observable takeLast(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError, int bufferSize) { return takeLast(Long.MAX_VALUE, time, unit, scheduler, delayError, bufferSize); } /** - * Returns an Observable that emits the items emitted by the source Observable until a second ObservableSource - * emits an item. + * Returns an {@code Observable} that emits the items emitted by the current {@code Observable} until a second {@link ObservableSource} + * emits an item or completes. *

- * + * *

*
Scheduler:
*
{@code takeUntil} does not operate by default on a particular {@link Scheduler}.
*
* * @param other - * the ObservableSource whose first emitted item will cause {@code takeUntil} to stop emitting items - * from the source Observable + * the {@code ObservableSource} whose first emitted item or completion will cause {@code takeUntil} to stop emitting items + * from the current {@code Observable} * @param * the type of items emitted by {@code other} - * @return an Observable that emits the items emitted by the source Observable until such time as {@code other} emits its first item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable takeUntil(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new ObservableTakeUntil(this, other)); + @NonNull + public final <@NonNull U> Observable takeUntil(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new ObservableTakeUntil<>(this, other)); } /** - * Returns an Observable that emits items emitted by the source Observable, checks the specified predicate + * Returns an {@code Observable} that emits items emitted by the current {@code Observable}, checks the specified predicate * for each item, and then completes when the condition is satisfied. *

- * + * *

* The difference between this operator and {@link #takeWhile(Predicate)} is that here, the condition is * evaluated after the item is emitted. @@ -13086,52 +14155,54 @@ public final Observable takeUntil(ObservableSource other) { *

* * @param stopPredicate - * a function that evaluates an item emitted by the source Observable and returns a Boolean - * @return an Observable that first emits items emitted by the source Observable, checks the specified - * condition after each item, and then completes when the condition is satisfied. + * a function that evaluates an item emitted by the current {@code Observable} and returns a {@link Boolean} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code stopPredicate} is {@code null} * @see ReactiveX operators documentation: TakeUntil * @see Observable#takeWhile(Predicate) * @since 1.1.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable takeUntil(Predicate stopPredicate) { - ObjectHelper.requireNonNull(stopPredicate, "stopPredicate is null"); - return RxJavaPlugins.onAssembly(new ObservableTakeUntilPredicate(this, stopPredicate)); + @NonNull + public final Observable takeUntil(@NonNull Predicate stopPredicate) { + Objects.requireNonNull(stopPredicate, "stopPredicate is null"); + return RxJavaPlugins.onAssembly(new ObservableTakeUntilPredicate<>(this, stopPredicate)); } /** - * Returns an Observable that emits items emitted by the source ObservableSource so long as each item satisfied a + * Returns an {@code Observable} that emits items emitted by the current {@code Observable} so long as each item satisfied a * specified condition, and then completes as soon as this condition is not satisfied. *

- * + * *

*
Scheduler:
*
{@code takeWhile} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates an item emitted by the source ObservableSource and returns a Boolean - * @return an Observable that emits the items from the source ObservableSource so long as each item satisfies the - * condition defined by {@code predicate}, then completes + * a function that evaluates an item emitted by the current {@code Observable} and returns a {@link Boolean} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: TakeWhile * @see Observable#takeUntil(Predicate) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable takeWhile(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new ObservableTakeWhile(this, predicate)); + @NonNull + public final Observable takeWhile(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new ObservableTakeWhile<>(this, predicate)); } /** - * Returns an Observable that emits only the first item emitted by the source ObservableSource during sequential + * Returns an {@code Observable} that emits only the first item emitted by the current {@code Observable} during sequential * time windows of a specified duration. *

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

- * + * *

*
Scheduler:
*
{@code throttleFirst} operates by default on the {@code computation} {@link Scheduler}.
@@ -13141,26 +14212,61 @@ public final Observable takeWhile(Predicate predicate) { * time to wait before emitting another item after emitting the last item * @param unit * the unit of time of {@code windowDuration} - * @return an Observable that performs the throttle operation + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable throttleFirst(long windowDuration, TimeUnit unit) { + @NonNull + public final Observable throttleFirst(long windowDuration, @NonNull TimeUnit unit) { return throttleFirst(windowDuration, unit, Schedulers.computation()); } /** - * Returns an Observable that emits only the first item emitted by the source ObservableSource during sequential - * time windows of a specified duration, where the windows are managed by a specified Scheduler. + * Returns an {@code Observable} that emits only the first item emitted by the current {@code Observable} during sequential + * time windows of a specified duration, where the windows are managed by a specified {@link Scheduler}. *

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

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param skipDuration + * time to wait before emitting another item after emitting the last item + * @param unit + * the unit of time of {@code skipDuration} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each + * event + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @see ReactiveX operators documentation: Sample + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + public final Observable throttleFirst(long skipDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableThrottleFirstTimed<>(this, skipDuration, unit, scheduler, null)); + } + + /** + * Returns an {@code Observable} that emits only the first item emitted by the current {@code Observable} during sequential + * time windows of a specified duration, where the windows are managed by a specified {@link Scheduler}. + *

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

+ * + *

+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param skipDuration @@ -13168,84 +14274,133 @@ public final Observable throttleFirst(long windowDuration, TimeUnit unit) { * @param unit * the unit of time of {@code skipDuration} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each * event - * @return an Observable that performs the throttle operation + * @param onDropped + * called when an item doesn't get delivered to the downstream + * + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} or {@code onDropped} is {@code null} * @see ReactiveX operators documentation: Sample + * @since 3.1.6 - Experimental */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableThrottleFirstTimed(this, skipDuration, unit, scheduler)); + @NonNull + @Experimental + public final Observable throttleFirst(long skipDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new ObservableThrottleFirstTimed<>(this, skipDuration, unit, scheduler, onDropped)); } /** - * Returns an Observable that emits only the last item emitted by the source ObservableSource during sequential + * Returns an {@code Observable} that emits only the last item emitted by the current {@code Observable} during sequential * time windows of a specified duration. *

* This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas - * {@link #throttleFirst} does not tick, it just tracks passage of time. + * {@code throttleFirst} does not tick, it just tracks passage of time. *

- * + * *

*
Scheduler:
*
{@code throttleLast} operates by default on the {@code computation} {@link Scheduler}.
*
* * @param intervalDuration - * duration of windows within which the last item emitted by the source ObservableSource will be + * duration of windows within which the last item emitted by the current {@code Observable} will be * emitted * @param unit * the unit of time of {@code intervalDuration} - * @return an Observable that performs the throttle operation + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Sample * @see #sample(long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable throttleLast(long intervalDuration, TimeUnit unit) { + @NonNull + public final Observable throttleLast(long intervalDuration, @NonNull TimeUnit unit) { return sample(intervalDuration, unit); } /** - * Returns an Observable that emits only the last item emitted by the source ObservableSource during sequential - * time windows of a specified duration, where the duration is governed by a specified Scheduler. + * Returns an {@code Observable} that emits only the last item emitted by the current {@code Observable} during sequential + * time windows of a specified duration, where the duration is governed by a specified {@link Scheduler}. *

* This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas - * {@link #throttleFirst} does not tick, it just tracks passage of time. + * {@code throttleFirst} does not tick, it just tracks passage of time. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param intervalDuration + * duration of windows within which the last item emitted by the current {@code Observable} will be + * emitted + * @param unit + * the unit of time of {@code intervalDuration} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each + * event + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Sample + * @see #sample(long, TimeUnit, Scheduler) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Observable throttleLast(long intervalDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + return sample(intervalDuration, unit, scheduler, false, onDropped); + } + + /** + * Returns an {@code Observable} that emits only the last item emitted by the current {@code Observable} during sequential + * time windows of a specified duration, where the duration is governed by a specified {@link Scheduler}. + *

+ * This differs from {@link #throttleFirst} in that this ticks along at a scheduled interval whereas + * {@code throttleFirst} does not tick, it just tracks passage of time. + *

+ * + *

+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param intervalDuration - * duration of windows within which the last item emitted by the source ObservableSource will be + * duration of windows within which the last item emitted by the current {@code Observable} will be * emitted * @param unit * the unit of time of {@code intervalDuration} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle timeout for each * event - * @return an Observable that performs the throttle operation + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Sample * @see #sample(long, TimeUnit, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable throttleLast(long intervalDuration, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return sample(intervalDuration, unit, scheduler); } /** - * Throttles items from the upstream {@code Observable} by first emitting the next + * Throttles items from the current {@code Observable} by first emitting the next * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* Unlike the option with {@link #throttleLatest(long, TimeUnit, boolean)}, the very last item being held back * (if any) is not emitted when the upstream completes. @@ -13260,23 +14415,25 @@ public final Observable throttleLast(long intervalDuration, TimeUnit unit, Sc * @param timeout the time to wait after an item emission towards the downstream * before trying to emit the latest item from upstream again * @param unit the time unit - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see #throttleLatest(long, TimeUnit, boolean) * @see #throttleLatest(long, TimeUnit, Scheduler) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable throttleLatest(long timeout, TimeUnit unit) { + @NonNull + public final Observable throttleLatest(long timeout, @NonNull TimeUnit unit) { return throttleLatest(timeout, unit, Schedulers.computation(), false); } /** - * Throttles items from the upstream {@code Observable} by first emitting the next + * Throttles items from the current {@code Observable} by first emitting the next * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* If no items were emitted from the upstream during this timeout phase, the next * upstream item is emitted immediately and the timeout window starts from then. @@ -13292,22 +14449,24 @@ public final Observable throttleLatest(long timeout, TimeUnit unit) { * immediately when the upstream completes, regardless if there is * a timeout window active or not. If {@code false}, the very last * upstream item is ignored and the flow terminates. - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see #throttleLatest(long, TimeUnit, Scheduler, boolean) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable throttleLatest(long timeout, TimeUnit unit, boolean emitLast) { + @NonNull + public final Observable throttleLatest(long timeout, @NonNull TimeUnit unit, boolean emitLast) { return throttleLatest(timeout, unit, Schedulers.computation(), emitLast); } /** - * Throttles items from the upstream {@code Observable} by first emitting the next + * Throttles items from the current {@code Observable} by first emitting the next * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* Unlike the option with {@link #throttleLatest(long, TimeUnit, Scheduler, boolean)}, the very last item being held back * (if any) is not emitted when the upstream completes. @@ -13322,24 +14481,26 @@ public final Observable throttleLatest(long timeout, TimeUnit unit, boolean e * @param timeout the time to wait after an item emission towards the downstream * before trying to emit the latest item from upstream again * @param unit the time unit - * @param scheduler the {@link Scheduler} where the timed wait and latest item + * @param scheduler the {@code Scheduler} where the timed wait and latest item * emission will be performed - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see #throttleLatest(long, TimeUnit, Scheduler, boolean) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable throttleLatest(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable throttleLatest(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return throttleLatest(timeout, unit, scheduler, false); } /** - * Throttles items from the upstream {@code Observable} by first emitting the next + * Throttles items from the current {@code Observable} by first emitting the next * item from upstream, then periodically emitting the latest item (if any) when * the specified timeout elapses between them. *

- * + * *

* If no items were emitted from the upstream during this timeout phase, the next * upstream item is emitted immediately and the timeout window starts from then. @@ -13351,135 +14512,229 @@ public final Observable throttleLatest(long timeout, TimeUnit unit, Scheduler * @param timeout the time to wait after an item emission towards the downstream * before trying to emit the latest item from upstream again * @param unit the time unit - * @param scheduler the {@link Scheduler} where the timed wait and latest item + * @param scheduler the {@code Scheduler} where the timed wait and latest item * emission will be performed * @param emitLast If {@code true}, the very last item from the upstream will be emitted * immediately when the upstream completes, regardless if there is * a timeout window active or not. If {@code false}, the very last * upstream item is ignored and the flow terminates. - * @return the new Observable instance + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable throttleLatest(long timeout, TimeUnit unit, Scheduler scheduler, boolean emitLast) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableThrottleLatest(this, timeout, unit, scheduler, emitLast)); + @NonNull + public final Observable throttleLatest(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableThrottleLatest<>(this, timeout, unit, scheduler, emitLast, null)); + } + + /** + * Throttles items from the current {@code Observable} by first emitting the next + * item from upstream, then periodically emitting the latest item (if any) when + * the specified timeout elapses between them, invoking the consumer for any dropped item. + *

+ * + *

+ * If no items were emitted from the upstream during this timeout phase, the next + * upstream item is emitted immediately and the timeout window starts from then. + *

+ *
Scheduler:
+ *
You specify which {@link Scheduler} this operator will use.
+ *
Error handling:
+ *
+ * If the upstream signals an {@code onError} or {@code onDropped} callback crashes, + * the error is delivered immediately to the downstream. If both happen, a {@link CompositeException} + * is created, containing both the upstream and the callback error. + * If the {@code onDropped} callback crashes when the sequence gets disposed, the exception is forwarded + * to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + *
+ *
+ * @param timeout the time to wait after an item emission towards the downstream + * before trying to emit the latest item from upstream again + * @param unit the time unit + * @param scheduler the {@code Scheduler} where the timed wait and latest item + * emission will be performed + * @param emitLast If {@code true}, the very last item from the upstream will be emitted + * immediately when the upstream completes, regardless if there is + * a timeout window active or not. If {@code false}, the very last + * upstream item is ignored and the flow terminates. + * @param onDropped called when an item is replaced by a newer item that doesn't get delivered + * to the downstream, including the very last item if {@code emitLast} is {@code false} + * and the current undelivered item when the sequence gets disposed. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code onDropped} is {@code null} + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Observable throttleLatest(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean emitLast, @NonNull Consumer onDropped) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(onDropped, "onDropped is null"); + return RxJavaPlugins.onAssembly(new ObservableThrottleLatest<>(this, timeout, unit, scheduler, emitLast, onDropped)); } /** - * Returns an Observable that mirrors the source ObservableSource, except that it drops items emitted by the - * source ObservableSource that are followed by newer items before a timeout value expires. The timer resets on + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by newer items before a timeout value expires. The timer resets on * each emission (alias to {@link #debounce(long, TimeUnit, Scheduler)}). *

- * Note: If items keep being emitted by the source ObservableSource faster than the timeout then no items - * will be emitted by the resulting ObservableSource. + * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items + * will be emitted by the resulting {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code throttleWithTimeout} operates by default on the {@code computation} {@link Scheduler}.
*
* * @param timeout - * the length of the window of time that must pass after the emission of an item from the source - * ObservableSource in which that ObservableSource emits no items in order for the item to be emitted by the - * resulting ObservableSource + * the length of the window of time that must pass after the emission of an item from the current + * {@code Observable}, in which the current {@code Observable} emits no items, in order for the item to be emitted by the + * resulting {@code Observable} * @param unit * the unit of time for the specified {@code timeout} - * @return an Observable that filters out items from the source ObservableSource that are too quickly followed by - * newer items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Debounce * @see #debounce(long, TimeUnit) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable throttleWithTimeout(long timeout, TimeUnit unit) { + @NonNull + public final Observable throttleWithTimeout(long timeout, @NonNull TimeUnit unit) { return debounce(timeout, unit); } /** - * Returns an Observable that mirrors the source ObservableSource, except that it drops items emitted by the - * source ObservableSource that are followed by newer items before a timeout value expires on a specified - * Scheduler. The timer resets on each emission (Alias to {@link #debounce(long, TimeUnit, Scheduler)}). + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission (Alias to {@link #debounce(long, TimeUnit, Scheduler)}). *

- * Note: If items keep being emitted by the source ObservableSource faster than the timeout then no items - * will be emitted by the resulting ObservableSource. + * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items + * will be emitted by the resulting {@code Observable}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout - * the length of the window of time that must pass after the emission of an item from the source - * ObservableSource in which that ObservableSource emits no items in order for the item to be emitted by the - * resulting ObservableSource + * the length of the window of time that must pass after the emission of an item from the current + * {@code Observable}, in which the current {@code Observable} emits no items, in order for the item to be emitted by the + * resulting {@code Observable} * @param unit * the unit of time for the specified {@code timeout} * @param scheduler - * the {@link Scheduler} to use internally to manage the timers that handle the timeout for each + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each * item - * @return an Observable that filters out items from the source ObservableSource that are too quickly followed by - * newer items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Debounce * @see #debounce(long, TimeUnit, Scheduler) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable throttleWithTimeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return debounce(timeout, unit, scheduler); } /** - * Returns an Observable that emits records of the time interval between consecutive items emitted by the - * source ObservableSource. + * Returns an {@code Observable} that mirrors the current {@code Observable}, except that it drops items emitted by the + * current {@code Observable} that are followed by newer items before a timeout value expires on a specified + * {@link Scheduler}. The timer resets on each emission (Alias to {@link #debounce(long, TimeUnit, Scheduler)}). + *

+ * Note: If items keep being emitted by the current {@code Observable} faster than the timeout then no items + * will be emitted by the resulting {@code Observable}. + *

+ * + *

+ *
Scheduler:
+ *
You specify which {@code Scheduler} this operator will use.
+ *
+ * + * @param timeout + * the length of the window of time that must pass after the emission of an item from the current + * {@code Observable}, in which the current {@code Observable} emits no items, in order for the item to be emitted by the + * resulting {@code Observable} + * @param unit + * the unit of time for the specified {@code timeout} + * @param scheduler + * the {@code Scheduler} to use internally to manage the timers that handle the timeout for each + * item + * @param onDropped + * called with the current entry when it has been replaced by a new one + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} or {@code onDropped} is {@code null} + * @see ReactiveX operators documentation: Debounce + * @see #debounce(long, TimeUnit, Scheduler, Consumer) + * @since 3.1.6 - Experimental + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull + @Experimental + public final Observable throttleWithTimeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull Consumer onDropped) { + return debounce(timeout, unit, scheduler, onDropped); + } + + /** + * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the + * current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code timeInterval} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
*
* - * @return an Observable that emits time interval information items + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable> timeInterval() { return timeInterval(TimeUnit.MILLISECONDS, Schedulers.computation()); } /** - * Returns an Observable that emits records of the time interval between consecutive items emitted by the - * source ObservableSource, where this interval is computed on a specified Scheduler. + * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the + * current {@code Observable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
The operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ * from the specified {@code Scheduler}. *
* * @param scheduler - * the {@link Scheduler} used to compute time intervals - * @return an Observable that emits time interval information items + * the {@code Scheduler} used to compute time intervals + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Observable> timeInterval(Scheduler scheduler) { + @NonNull + public final Observable> timeInterval(@NonNull Scheduler scheduler) { return timeInterval(TimeUnit.MILLISECONDS, scheduler); } /** - * Returns an Observable that emits records of the time interval between consecutive items emitted by the - * source ObservableSource. + * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the + * current {@code Observable}. *

- * + * *

*
Scheduler:
*
{@code timeInterval} does not operate on any particular scheduler but uses the current time @@ -13487,47 +14742,51 @@ public final Observable> timeInterval(Scheduler scheduler) { *
* * @param unit the time unit for the current time - * @return an Observable that emits time interval information items + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> timeInterval(TimeUnit unit) { + @NonNull + public final Observable> timeInterval(@NonNull TimeUnit unit) { return timeInterval(unit, Schedulers.computation()); } /** - * Returns an Observable that emits records of the time interval between consecutive items emitted by the - * source ObservableSource, where this interval is computed on a specified Scheduler. + * Returns an {@code Observable} that emits records of the time interval between consecutive items emitted by the + * current {@code Observable}, where this interval is computed on a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
The operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ * from the specified {@code Scheduler}. *
* * @param unit the time unit for the current time * @param scheduler - * the {@link Scheduler} used to compute time intervals - * @return an Observable that emits time interval information items + * the {@code Scheduler} used to compute time intervals + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: TimeInterval */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Observable> timeInterval(TimeUnit unit, Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableTimeInterval(this, unit, scheduler)); + @NonNull + public final Observable> timeInterval(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableTimeInterval<>(this, unit, scheduler)); } /** - * Returns an Observable that mirrors the source ObservableSource, but notifies observers of a - * {@code TimeoutException} if an item emitted by the source ObservableSource doesn't arrive within a window of - * time after the emission of the previous item, where that period of time is measured by an ObservableSource that + * Returns an {@code Observable} that mirrors the current {@code Observable}, but notifies observers of a + * {@link TimeoutException} if an item emitted by the current {@code 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 {@link ObservableSource} that * is a function of the previous item. *

- * + * *

* Note: The arrival of the first source item is never timed out. *

@@ -13538,26 +14797,26 @@ public final Observable> timeInterval(TimeUnit unit, Scheduler schedule * @param * the timeout value type (ignored) * @param itemTimeoutIndicator - * a function that returns an ObservableSource for each item emitted by the source - * ObservableSource and that determines the timeout window for the subsequent item - * @return an Observable that mirrors the source ObservableSource, but notifies observers of a - * {@code TimeoutException} if an item emitted by the source ObservableSource takes longer to arrive than - * the time window defined by the selector for the previously emitted item + * a function that returns an {@code ObservableSource} for each item emitted by the current + * {@code Observable} and that determines the timeout window for the subsequent item + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code itemTimeoutIndicator} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable timeout(Function> itemTimeoutIndicator) { + @NonNull + public final <@NonNull V> Observable timeout(@NonNull Function> itemTimeoutIndicator) { return timeout0(null, itemTimeoutIndicator, null); } /** - * Returns an Observable that mirrors the source ObservableSource, but that switches to a fallback ObservableSource if - * an item emitted by the source ObservableSource doesn't arrive within a window of time after the emission of the - * previous item, where that period of time is measured by an ObservableSource that is a function of the previous + * Returns an {@code Observable} that mirrors the current {@code Observable}, but that switches to a fallback {@link ObservableSource} if + * an item emitted by the current {@code 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 {@code ObservableSource} that is a function of the previous * item. *

- * + * *

* Note: The arrival of the first source item is never timed out. *

@@ -13568,29 +14827,29 @@ public final Observable timeout(Function * the timeout value type (ignored) * @param itemTimeoutIndicator - * a function that returns an ObservableSource, for each item emitted by the source ObservableSource, that + * a function that returns an {@code ObservableSource}, for each item emitted by the current {@code Observable}, that * determines the timeout window for the subsequent item - * @param other - * the fallback ObservableSource to switch to if the source ObservableSource times out - * @return an Observable that mirrors the source ObservableSource, but switches to mirroring a fallback ObservableSource - * if an item emitted by the source ObservableSource takes longer to arrive than the time window defined - * by the selector for the previously emitted item + * @param fallback + * the fallback {@code ObservableSource} to switch to if the current {@code Observable} times out + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code itemTimeoutIndicator} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable timeout(Function> itemTimeoutIndicator, - ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(null, itemTimeoutIndicator, other); + @NonNull + public final <@NonNull V> Observable timeout(@NonNull Function> itemTimeoutIndicator, + @NonNull ObservableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(null, itemTimeoutIndicator, fallback); } /** - * Returns an Observable that mirrors the source ObservableSource but applies a timeout policy for each emitted + * Returns an {@code Observable} that mirrors the current {@code Observable} but applies a timeout policy for each emitted * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, - * the resulting ObservableSource terminates and notifies observers of a {@code TimeoutException}. + * the resulting {@code Observable} terminates and notifies observers of a {@link TimeoutException}. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -13598,25 +14857,26 @@ public final Observable timeout(FunctionReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable timeout(long timeout, TimeUnit timeUnit) { - return timeout0(timeout, timeUnit, null, Schedulers.computation()); + @NonNull + public final Observable timeout(long timeout, @NonNull TimeUnit unit) { + return timeout0(timeout, unit, null, Schedulers.computation()); } /** - * Returns an Observable that mirrors the source ObservableSource but applies a timeout policy for each emitted + * Returns an {@code Observable} that mirrors the current {@code Observable} but applies a timeout policy for each emitted * item. If the next item isn't emitted within the specified timeout duration starting from its predecessor, - * the source ObservableSource is disposed and resulting ObservableSource begins instead - * to mirror a fallback ObservableSource. + * the current {@code Observable} is disposed and the resulting {@code Observable} begins instead + * to mirror a fallback {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code computation} {@link Scheduler}.
@@ -13624,85 +14884,89 @@ public final Observable timeout(long timeout, TimeUnit timeUnit) { * * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument - * @param other - * the fallback ObservableSource to use in case of a timeout - * @return the source ObservableSource modified to switch to the fallback ObservableSource in case of a timeout + * @param fallback + * the fallback {@code ObservableSource} to use in case of a timeout + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable timeout(long timeout, TimeUnit timeUnit, ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, timeUnit, other, Schedulers.computation()); + @NonNull + public final Observable timeout(long timeout, @NonNull TimeUnit unit, @NonNull ObservableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, fallback, Schedulers.computation()); } /** - * Returns an Observable that mirrors the source ObservableSource but applies a timeout policy for each emitted - * item using a specified Scheduler. If the next item isn't emitted within the specified timeout duration - * starting from its predecessor, the source ObservableSource is disposed and resulting ObservableSource - * begins instead to mirror a fallback ObservableSource. + * Returns an {@code Observable} that mirrors the current {@code Observable} but applies a timeout policy for each emitted + * item using a specified {@link Scheduler}. If the next item isn't emitted within the specified timeout duration + * starting from its predecessor, the current {@code Observable} is disposed and returned {@code Observable} + * begins instead to mirror a fallback {@link ObservableSource}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param scheduler - * the {@link Scheduler} to run the timeout timers on - * @param other - * the ObservableSource to use as the fallback in case of a timeout - * @return the source ObservableSource modified so that it will switch to the fallback ObservableSource in case of a - * timeout + * the {@code Scheduler} to run the timeout timers on + * @param fallback + * the {@code ObservableSource} to use as the fallback in case of a timeout + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler, ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, timeUnit, other, scheduler); + @NonNull + public final Observable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull ObservableSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, fallback, scheduler); } /** - * Returns an Observable that mirrors the source ObservableSource but applies a timeout policy for each emitted - * item, where this policy is governed on a specified Scheduler. If the next item isn't emitted within the - * specified timeout duration starting from its predecessor, the resulting ObservableSource terminates and - * notifies observers of a {@code TimeoutException}. + * Returns an {@code Observable} that mirrors the current {@code Observable} but applies a timeout policy for each emitted + * item, where this policy is governed on a specified {@link Scheduler}. If the next item isn't emitted within the + * specified timeout duration starting from its predecessor, the resulting {@code Observable} terminates and + * notifies observers of a {@link TimeoutException}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param timeout * maximum duration between items before a timeout occurs - * @param timeUnit + * @param unit * the unit of time that applies to the {@code timeout} argument * @param scheduler - * the Scheduler to run the timeout timers on - * @return the source ObservableSource modified to notify observers of a {@code TimeoutException} in case of a - * timeout + * the {@code Scheduler} to run the timeout timers on + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable timeout(long timeout, TimeUnit timeUnit, Scheduler scheduler) { - return timeout0(timeout, timeUnit, null, scheduler); + @NonNull + public final Observable timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + return timeout0(timeout, unit, null, scheduler); } /** - * Returns an Observable that mirrors the source ObservableSource, but notifies observers of a - * {@code TimeoutException} if either the first item emitted by the source ObservableSource or any subsequent item - * doesn't arrive within time windows defined by other ObservableSources. + * Returns an {@code Observable} that mirrors the current {@code Observable}, but notifies observers of a + * {@link TimeoutException} if either the first item emitted by the current {@code Observable} or any subsequent item + * doesn't arrive within time windows defined by indicator {@link ObservableSource}s. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code immediate} {@link Scheduler}.
@@ -13713,31 +14977,31 @@ public final Observable timeout(long timeout, TimeUnit timeUnit, Scheduler sc * @param * the subsequent timeout value type (ignored) * @param firstTimeoutIndicator - * a function that returns an ObservableSource that determines the timeout window for the first source + * a function that returns an {@code ObservableSource} that determines the timeout window for the first source * item * @param itemTimeoutIndicator - * a function that returns an ObservableSource for each item emitted by the source ObservableSource and that + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code 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 ObservableSource, but notifies observers of a - * {@code TimeoutException} if either the first item or any subsequent item doesn't arrive within - * the time windows specified by the timeout selectors + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code firstTimeoutIndicator} or {@code itemTimeoutIndicator} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable timeout(ObservableSource firstTimeoutIndicator, - Function> itemTimeoutIndicator) { - ObjectHelper.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); + @NonNull + public final <@NonNull U, @NonNull V> Observable timeout(@NonNull ObservableSource firstTimeoutIndicator, + @NonNull Function> itemTimeoutIndicator) { + Objects.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); return timeout0(firstTimeoutIndicator, itemTimeoutIndicator, null); } /** - * Returns an Observable that mirrors the source ObservableSource, but switches to a fallback ObservableSource if either - * the first item emitted by the source ObservableSource or any subsequent item doesn't arrive within time windows - * defined by other ObservableSources. + * Returns an {@code Observable} that mirrors the current {@code Observable}, but switches to a fallback {@link ObservableSource} if either + * the first item emitted by the current {@code Observable} or any subsequent item doesn't arrive within time windows + * defined by indicator {@code ObservableSource}s. *

- * + * *

*
Scheduler:
*
This version of {@code timeout} operates by default on the {@code immediate} {@link Scheduler}.
@@ -13748,96 +15012,99 @@ public final Observable timeout(ObservableSource firstTimeoutIndica * @param * the subsequent timeout value type (ignored) * @param firstTimeoutIndicator - * a function that returns an ObservableSource which determines the timeout window for the first source + * a function that returns an {@code ObservableSource} which determines the timeout window for the first source * item * @param itemTimeoutIndicator - * a function that returns an ObservableSource for each item emitted by the source ObservableSource and that + * a function that returns an {@code ObservableSource} for each item emitted by the current {@code 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 ObservableSource to switch to if the source ObservableSource times out - * @return an Observable that mirrors the source ObservableSource, but switches to the {@code other} ObservableSource if - * either the first item emitted by the source ObservableSource or any subsequent item doesn't arrive - * within time windows defined by the timeout selectors + * @param fallback + * the fallback {@code ObservableSource} to switch to if the current {@code Observable} times out + * @return the new {@code Observable} instance * @throws NullPointerException - * if {@code itemTimeoutIndicator} is null, or - * if {@code other} is null + * if {@code firstTimeoutIndicator}, {@code itemTimeoutIndicator} or {@code fallback} is {@code null} * @see ReactiveX operators documentation: Timeout */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable timeout( - ObservableSource firstTimeoutIndicator, - Function> itemTimeoutIndicator, - ObservableSource other) { - ObjectHelper.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(firstTimeoutIndicator, itemTimeoutIndicator, other); + @NonNull + public final <@NonNull U, @NonNull V> Observable timeout( + @NonNull ObservableSource firstTimeoutIndicator, + @NonNull Function> itemTimeoutIndicator, + @NonNull ObservableSource fallback) { + Objects.requireNonNull(firstTimeoutIndicator, "firstTimeoutIndicator is null"); + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(firstTimeoutIndicator, itemTimeoutIndicator, fallback); } - private Observable timeout0(long timeout, TimeUnit timeUnit, ObservableSource other, - Scheduler scheduler) { - ObjectHelper.requireNonNull(timeUnit, "timeUnit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableTimeoutTimed(this, timeout, timeUnit, scheduler, other)); + @NonNull + private Observable timeout0(long timeout, @NonNull TimeUnit unit, + @Nullable ObservableSource fallback, + @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableTimeoutTimed<>(this, timeout, unit, scheduler, fallback)); } + @NonNull private Observable timeout0( - ObservableSource firstTimeoutIndicator, - Function> itemTimeoutIndicator, - ObservableSource other) { - ObjectHelper.requireNonNull(itemTimeoutIndicator, "itemTimeoutIndicator is null"); - return RxJavaPlugins.onAssembly(new ObservableTimeout(this, firstTimeoutIndicator, itemTimeoutIndicator, other)); + @NonNull ObservableSource firstTimeoutIndicator, + @NonNull Function> itemTimeoutIndicator, + @Nullable ObservableSource fallback) { + Objects.requireNonNull(itemTimeoutIndicator, "itemTimeoutIndicator is null"); + return RxJavaPlugins.onAssembly(new ObservableTimeout<>(this, firstTimeoutIndicator, itemTimeoutIndicator, fallback)); } /** - * Returns an Observable that emits each item emitted by the source ObservableSource, wrapped in a + * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Scheduler:
*
{@code timestamp} does not operate on any particular scheduler but uses the current time * from the {@code computation} {@link Scheduler}.
*
* - * @return an Observable that emits timestamped items from the source ObservableSource + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable> timestamp() { return timestamp(TimeUnit.MILLISECONDS, Schedulers.computation()); } /** - * Returns an Observable that emits each item emitted by the source ObservableSource, wrapped in a - * {@link Timed} object whose timestamps are provided by a specified Scheduler. + * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a + * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
This operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ * from the specified {@code Scheduler}. *
* * @param scheduler - * the {@link Scheduler} to use as a time source - * @return an Observable that emits timestamped items from the source ObservableSource with timestamps provided by - * the {@code scheduler} + * the {@code Scheduler} to use as a time source + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Observable> timestamp(Scheduler scheduler) { + @NonNull + public final Observable> timestamp(@NonNull Scheduler scheduler) { return timestamp(TimeUnit.MILLISECONDS, scheduler); } /** - * Returns an Observable that emits each item emitted by the source ObservableSource, wrapped in a + * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a * {@link Timed} object. *

- * + * *

*
Scheduler:
*
{@code timestamp} does not operate on any particular scheduler but uses the current time @@ -13845,39 +15112,42 @@ public final Observable> timestamp(Scheduler scheduler) { *
* * @param unit the time unit for the current time - * @return an Observable that emits timestamped items from the source ObservableSource + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> timestamp(TimeUnit unit) { + @NonNull + public final Observable> timestamp(@NonNull TimeUnit unit) { return timestamp(unit, Schedulers.computation()); } /** - * Returns an Observable that emits each item emitted by the source ObservableSource, wrapped in a - * {@link Timed} object whose timestamps are provided by a specified Scheduler. + * Returns an {@code Observable} that emits each item emitted by the current {@code Observable}, wrapped in a + * {@link Timed} object whose timestamps are provided by a specified {@link Scheduler}. *

- * + * *

*
Scheduler:
*
This operator does not operate on any particular scheduler but uses the current time - * from the specified {@link Scheduler}.
+ * from the specified {@code Scheduler}. *
* * @param unit the time unit for the current time * @param scheduler - * the {@link Scheduler} to use as a time source - * @return an Observable that emits timestamped items from the source ObservableSource with timestamps provided by - * the {@code scheduler} + * the {@code Scheduler} to use as a time source + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Timestamp */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) // Supplied scheduler is only used for creating timestamps. - public final Observable> timestamp(final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return map(Functions.timestampWith(unit, scheduler)); + @NonNull + public final Observable> timestamp(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return map(Functions.timestampWith(unit, scheduler)); } /** @@ -13890,95 +15160,97 @@ public final Observable> timestamp(final TimeUnit unit, final Scheduler *
*

History: 2.1.7 - experimental * @param the resulting object type - * @param converter the function that receives the current Observable instance and returns a value + * @param converter the function that receives the current {@code Observable} instance and returns a value * @return the converted value - * @throws NullPointerException if converter is null + * @throws NullPointerException if {@code converter} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final R to(@NonNull ObservableConverter converter) { - return ObjectHelper.requireNonNull(converter, "converter is null").apply(this); + @NonNull + public final <@NonNull R> R to(@NonNull ObservableConverter converter) { + return Objects.requireNonNull(converter, "converter is null").apply(this); } /** - * Returns a Single that emits a single item, a list composed of all the items emitted by the - * finite source ObservableSource. + * Returns a {@link Single} that emits a single item, a {@link List} composed of all the items emitted by the + * current and finite {@code Observable}. *

- * + * *

- * Normally, an ObservableSource that returns multiple items will do so by invoking its {@link Observer}'s - * {@link Observer#onNext onNext} method for each such item. You can change this behavior, instructing the - * ObservableSource to compose a list of all of these items and then to invoke the Observer's {@code onNext} - * function once, passing it the entire list, by calling the ObservableSource's {@code toList} method prior to + * Normally, an {@link ObservableSource} that returns multiple items will do so by invoking its {@link Observer}'s + * {@link Observer#onNext onNext} method for each such item. You can change this behavior by having the + * operator to compose a list of all of these items and then to invoke the {@link SingleObserver}'s {@code onSuccess} + * method once, passing it the entire list, by calling the {@code Observable}'s {@code toList} method prior to * calling its {@link #subscribe} method. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toList} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a Single that emits a single item: a List containing all of the items emitted by the source - * ObservableSource + * @return the new {@code Single} instance * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toList() { + @NonNull + public final Single<@NonNull List> toList() { return toList(16); } /** - * Returns a Single that emits a single item, a list composed of all the items emitted by the - * finite source ObservableSource. + * Returns a {@link Single} that emits a single item, a {@link List} composed of all the items emitted by the + * current and finite {@code Observable}. *

- * + * *

- * Normally, an ObservableSource that returns multiple items will do so by invoking its {@link Observer}'s - * {@link Observer#onNext onNext} method for each such item. You can change this behavior, instructing the - * ObservableSource to compose a list of all of these items and then to invoke the Observer's {@code onNext} - * function once, passing it the entire list, by calling the ObservableSource's {@code toList} method prior to + * Normally, an {@link ObservableSource} that returns multiple items will do so by invoking its {@link Observer}'s + * {@link Observer#onNext onNext} method for each such item. You can change this behavior by having the + * operator to compose a list of all of these items and then to invoke the {@link SingleObserver}'s {@code onSuccess} + * method once, passing it the entire list, by calling the {@code Observable}'s {@code toList} method prior to * calling its {@link #subscribe} method. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toList} does not operate by default on a particular {@link Scheduler}.
*
* * @param capacityHint - * the number of elements expected from the current Observable - * @return a Single that emits a single item: a List containing all of the items emitted by the source - * ObservableSource + * the number of elements expected from the current {@code Observable} + * @return the new {@code Single} instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toList(final int capacityHint) { + @NonNull + public final Single<@NonNull List> toList(int capacityHint) { ObjectHelper.verifyPositive(capacityHint, "capacityHint"); - return RxJavaPlugins.onAssembly(new ObservableToListSingle>(this, capacityHint)); + return RxJavaPlugins.onAssembly(new ObservableToListSingle<>(this, capacityHint)); } /** - * Returns a Single that emits a single item, a list composed of all the items emitted by the - * finite source ObservableSource. + * Returns a {@link Single} that emits a single item, a {@link Collection} (subclass) composed of all the items emitted by the + * finite upstream {@code Observable}. *

* *

- * Normally, an ObservableSource that returns multiple items will do so by invoking its {@link Observer}'s - * {@link Observer#onNext onNext} method for each such item. You can change this behavior, instructing the - * ObservableSource to compose a list of all of these items and then to invoke the Observer's {@code onNext} - * function once, passing it the entire list, by calling the ObservableSource's {@code toList} method prior to + * Normally, an {@link ObservableSource} that returns multiple items will do so by invoking its {@link Observer}'s + * {@link Observer#onNext onNext} method for each such item. You can change this behavior by having the + * operator to compose a collection of all of these items and then to invoke the {@link SingleObserver}'s {@code onSuccess} + * method once, passing it the entire collection, by calling the {@code Observable}'s {@code toList} method prior to * calling its {@link #subscribe} method. *

* Note that this operator requires the upstream to signal {@code onComplete} for the accumulated collection to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toList} does not operate by default on a particular {@link Scheduler}.
@@ -13986,30 +15258,31 @@ public final Single> toList(final int capacityHint) { * * @param the subclass of a collection of Ts * @param collectionSupplier - * the Supplier returning the collection (for each individual Observer) to be filled in - * @return a Single that emits a single item: a List containing all of the items emitted by the source - * ObservableSource + * the {@link Supplier} returning the collection (for each individual {@code Observer}) to be filled in + * @return the new {@code Single} instance + * @throws NullPointerException if {@code collectionSupplier} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > Single toList(Supplier collectionSupplier) { - ObjectHelper.requireNonNull(collectionSupplier, "collectionSupplier is null"); - return RxJavaPlugins.onAssembly(new ObservableToListSingle(this, collectionSupplier)); + @NonNull + public final <@NonNull U extends Collection> Single toList(@NonNull Supplier collectionSupplier) { + Objects.requireNonNull(collectionSupplier, "collectionSupplier is null"); + return RxJavaPlugins.onAssembly(new ObservableToListSingle<>(this, collectionSupplier)); } /** - * Returns a Single that emits a single HashMap containing all items emitted by the - * finite source ObservableSource, mapped by the keys returned by a specified + * Returns a {@link Single} that emits a single {@link HashMap} containing all items emitted by the + * current and finite {@code Observable}, mapped by the keys returned by a specified * {@code keySelector} function. *

- * + * *

- * If more than one source item maps to the same key, the HashMap will contain the latest of those items. + * If more than one source item maps to the same key, the {@code HashMap} will contain the latest of those items. *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code HashMap} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMap} does not operate by default on a particular {@link Scheduler}.
@@ -14017,235 +15290,245 @@ public final > Single toList(Supplier coll * * @param the key type of the Map * @param keySelector - * the function that extracts the key from a source item to be used in the HashMap - * @return a Single that emits a single item: a HashMap containing the mapped items from the source - * ObservableSource + * the function that extracts the key from a source item to be used in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toMap(final Function keySelector) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - return collect(HashMapSupplier.asSupplier(), Functions.toMapKeySelector(keySelector)); + @NonNull + public final <@NonNull K> Single<@NonNull Map> toMap(@NonNull Function keySelector) { + Objects.requireNonNull(keySelector, "keySelector is null"); + return collect(HashMapSupplier.asSupplier(), Functions.toMapKeySelector(keySelector)); } /** - * Returns a Single that emits a single HashMap containing values corresponding to items emitted by the - * finite source ObservableSource, mapped by the keys returned by a specified {@code keySelector} function. + * Returns a {@link Single} that emits a single {@link HashMap} containing values corresponding to items emitted by the + * current and finite {@code Observable}, mapped by the keys and values returned by the given selector functions. *

- * + * *

- * If more than one source item maps to the same key, the HashMap will contain a single entry that + * If more than one source item maps to the same key, the {@code HashMap} will contain a single entry that * corresponds to the latest of those items. *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code HashMap} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map - * @param the value type of the Map + * @param the key type of the {@code HashMap} + * @param the value type of the {@code HashMap} * @param keySelector - * the function that extracts the key from a source item to be used in the HashMap + * the function that extracts the key from a source item to be used in the {@code HashMap} * @param valueSelector - * the function that extracts the value from a source item to be used in the HashMap - * @return a Single that emits a single item: a HashMap containing the mapped items from the source - * ObservableSource + * the function that extracts the value from a source item to be used in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toMap( - final Function keySelector, - final Function valueSelector) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); - return collect(HashMapSupplier.asSupplier(), Functions.toMapKeyValueSelector(keySelector, valueSelector)); + @NonNull + public final <@NonNull K, @NonNull V> Single> toMap( + @NonNull Function keySelector, + @NonNull Function valueSelector) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); + return collect(HashMapSupplier.asSupplier(), Functions.toMapKeyValueSelector(keySelector, valueSelector)); } /** - * Returns a Single that emits a single Map, returned by a specified {@code mapFactory} function, that - * contains keys and values extracted from the items emitted by the finite source ObservableSource. + * Returns a {@link Single} that emits a single {@link Map} (subclass), returned by a specified {@code mapFactory} function, that + * contains keys and values extracted from the items, via selector functions, emitted by the current and finite {@code Observable}. *

- * + * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code Map} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map - * @param the value type of the Map + * @param the key type of the {@code Map} + * @param the value type of the {@code Map} * @param keySelector - * the function that extracts the key from a source item to be used in the Map + * the function that extracts the key from a source item to be used in the {@code Map} * @param valueSelector - * the function that extracts the value from the source items to be used as value in the Map + * the function that extracts the value from the source items to be used as value in the {@code Map} * @param mapSupplier - * the function that returns a Map instance to be used - * @return a Single that emits a single item: a Map that contains the mapped items emitted by the - * source ObservableSource + * the function that returns a {@code Map} instance to be used + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector}, {@code valueSelector} or {@code mapSupplier} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toMap( - final Function keySelector, - final Function valueSelector, - Supplier> mapSupplier) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); - ObjectHelper.requireNonNull(mapSupplier, "mapSupplier is null"); + @NonNull + public final <@NonNull K, @NonNull V> Single> toMap( + @NonNull Function keySelector, + @NonNull Function valueSelector, + @NonNull Supplier> mapSupplier) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); + Objects.requireNonNull(mapSupplier, "mapSupplier is null"); return collect(mapSupplier, Functions.toMapKeyValueSelector(keySelector, valueSelector)); } /** - * Returns a Single that emits a single HashMap that contains an ArrayList of items emitted by the - * finite source ObservableSource keyed by a specified {@code keySelector} function. + * Returns a {@link Single} that emits a single {@link HashMap} that contains an {@link ArrayList} of items emitted by the + * current and finite {@code Observable} keyed by a specified {@code keySelector} function. *

- * + * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code HashMap} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map + * @param the key type of the {@code HashMap} * @param keySelector - * the function that extracts the key from the source items to be used as key in the HashMap - * @return a Single that emits a single item: a HashMap that contains an ArrayList of items mapped from - * the source ObservableSource + * the function that extracts the key from the source items to be used as key in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap(Function keySelector) { - @SuppressWarnings({ "rawtypes", "unchecked" }) - Function valueSelector = (Function)Functions.identity(); + @NonNull + public final <@NonNull K> Single<@NonNull Map>> toMultimap(@NonNull Function keySelector) { + Function valueSelector = Functions.identity(); Supplier>> mapSupplier = HashMapSupplier.asSupplier(); Function> collectionFactory = ArrayListSupplier.asFunction(); return toMultimap(keySelector, valueSelector, mapSupplier, collectionFactory); } /** - * Returns a Single that emits a single HashMap that contains an ArrayList of values extracted by a - * specified {@code valueSelector} function from items emitted by the finite source ObservableSource, + * Returns a {@link Single} that emits a single {@link HashMap} that contains an {@link ArrayList} of values extracted by a + * specified {@code valueSelector} function from items emitted by the current and finite {@code Observable}, * keyed by a specified {@code keySelector} function. *

- * + * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code HashMap} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map - * @param the value type of the Map + * @param the key type of the {@code HashMap} + * @param the value type of the {@code HashMap} * @param keySelector - * the function that extracts a key from the source items to be used as key in the HashMap + * the function that extracts a key from the source items to be used as key in the {@code HashMap} * @param valueSelector - * the function that extracts a value from the source items to be used as value in the HashMap - * @return a Single that emits a single item: a HashMap that contains an ArrayList of items mapped from - * the source ObservableSource + * the function that extracts a value from the source items to be used as value in the {@code HashMap} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector} or {@code valueSelector} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap(Function keySelector, Function valueSelector) { + @NonNull + public final <@NonNull K, @NonNull V> Single<@NonNull Map>> toMultimap(@NonNull Function keySelector, Function valueSelector) { Supplier>> mapSupplier = HashMapSupplier.asSupplier(); Function> collectionFactory = ArrayListSupplier.asFunction(); return toMultimap(keySelector, valueSelector, mapSupplier, collectionFactory); } /** - * Returns a Single that emits a single Map, returned by a specified {@code mapFactory} function, that - * contains a custom collection of values, extracted by a specified {@code valueSelector} function from - * items emitted by the source ObservableSource, and keyed by the {@code keySelector} function. + * Returns a {@link Single} that emits a single {@code Map} (subclass), returned by a specified {@code mapFactory} function, that + * contains a custom {@link Collection} of values, extracted by a specified {@code valueSelector} function from + * items emitted by the current and finite {@code Observable}, and keyed by the {@code keySelector} function. *

- * + * + *

+ * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code Map} to + * be emitted. Sources that are infinite and never complete will never emit anything through this + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map - * @param the value type of the Map + * @param the key type of the {@code Map} + * @param the value type of the {@code Map} * @param keySelector - * the function that extracts a key from the source items to be used as the key in the Map + * the function that extracts a key from the source items to be used as the key in the {@code Map} * @param valueSelector - * the function that extracts a value from the source items to be used as the value in the Map + * the function that extracts a value from the source items to be used as the value in the {@code Map} * @param mapSupplier - * the function that returns a Map instance to be used + * the function that returns a {@code Map} instance to be used * @param collectionFactory - * the function that returns a Collection instance for a particular key to be used in the Map - * @return a Single that emits a single item: a Map that contains the collection of mapped items from - * the source ObservableSource + * the function that returns a {@code Collection} instance for a particular key to be used in the {@code Map} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector}, {@code valueSelector}, {@code mapSupplier} or {@code collectionFactory} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap( - final Function keySelector, - final Function valueSelector, - final Supplier>> mapSupplier, - final Function> collectionFactory) { - ObjectHelper.requireNonNull(keySelector, "keySelector is null"); - ObjectHelper.requireNonNull(valueSelector, "valueSelector is null"); - ObjectHelper.requireNonNull(mapSupplier, "mapSupplier is null"); - ObjectHelper.requireNonNull(collectionFactory, "collectionFactory is null"); + @NonNull + public final <@NonNull K, @NonNull V> Single<@NonNull Map>> toMultimap( + @NonNull Function keySelector, + @NonNull Function valueSelector, + @NonNull Supplier>> mapSupplier, + @NonNull Function> collectionFactory) { + Objects.requireNonNull(keySelector, "keySelector is null"); + Objects.requireNonNull(valueSelector, "valueSelector is null"); + Objects.requireNonNull(mapSupplier, "mapSupplier is null"); + Objects.requireNonNull(collectionFactory, "collectionFactory is null"); return collect(mapSupplier, Functions.toMultimapKeyValueSelector(keySelector, valueSelector, collectionFactory)); } /** - * Returns a Single that emits a single Map, returned by a specified {@code mapFactory} function, that - * contains an ArrayList of values, extracted by a specified {@code valueSelector} function from items - * emitted by the finite source ObservableSource and keyed by the {@code keySelector} function. + * Returns a {@link Single} that emits a single {@link Map} (subclass), returned by a specified {@code mapFactory} function, that + * contains an {@link ArrayList} of values, extracted by a specified {@code valueSelector} function from items + * emitted by the current and finite {@code Observable} and keyed by the {@code keySelector} function. *

- * + * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated map to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code Map} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toMultimap} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the key type of the Map - * @param the value type of the Map + * @param the key type of the {@code Map} + * @param the value type of the {@code Map} * @param keySelector - * the function that extracts a key from the source items to be used as the key in the Map + * the function that extracts a key from the source items to be used as the key in the {@code Map} * @param valueSelector - * the function that extracts a value from the source items to be used as the value in the Map + * the function that extracts a value from the source items to be used as the value in the {@code Map} * @param mapSupplier - * the function that returns a Map instance to be used - * @return a Single that emits a single item: a Map that contains a list items mapped from the source - * ObservableSource + * the function that returns a {@code Map} instance to be used + * @return the new {@code Single} instance + * @throws NullPointerException if {@code keySelector}, {@code valueSelector} or {@code mapSupplier} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single>> toMultimap( - Function keySelector, - Function valueSelector, - Supplier>> mapSupplier - ) { - return toMultimap(keySelector, valueSelector, mapSupplier, ArrayListSupplier.asFunction()); + @NonNull + public final <@NonNull K, @NonNull V> Single<@NonNull Map>> toMultimap( + @NonNull Function keySelector, + @NonNull Function valueSelector, + @NonNull Supplier>> mapSupplier + ) { + return toMultimap(keySelector, valueSelector, mapSupplier, ArrayListSupplier.asFunction()); } /** - * Converts the current Observable into a Flowable by applying the specified backpressure strategy. + * Converts the current {@code Observable} into a {@link Flowable} by applying the specified backpressure strategy. *

* Marble diagrams for the various backpressure strategies are as follows: *

    @@ -14259,7 +15542,7 @@ public final Single>> toMultimap( * *
  • {@link BackpressureStrategy#LATEST} *

    - * + * *

  • *
  • {@link BackpressureStrategy#ERROR} *

    @@ -14267,7 +15550,7 @@ public final Single>> toMultimap( *

  • *
  • {@link BackpressureStrategy#MISSING} *

    - * + * *

  • *
*
@@ -14278,14 +15561,16 @@ public final Single>> toMultimap( *
* * @param strategy the backpressure strategy to apply - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code strategy} is {@code null} */ @BackpressureSupport(BackpressureKind.SPECIAL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable toFlowable(BackpressureStrategy strategy) { - Flowable f = new FlowableFromObservable(this); - + @NonNull + public final Flowable toFlowable(@NonNull BackpressureStrategy strategy) { + Objects.requireNonNull(strategy, "strategy is null"); + Flowable f = new FlowableFromObservable<>(this); switch (strategy) { case DROP: return f.onBackpressureDrop(); @@ -14294,161 +15579,170 @@ public final Flowable toFlowable(BackpressureStrategy strategy) { case MISSING: return f; case ERROR: - return RxJavaPlugins.onAssembly(new FlowableOnBackpressureError(f)); + return RxJavaPlugins.onAssembly(new FlowableOnBackpressureError<>(f)); default: return f.onBackpressureBuffer(); } } /** - * Returns a Single that emits a list that contains the items emitted by the finite source ObservableSource, in a - * sorted order. Each item emitted by the ObservableSource must implement {@link Comparable} with respect to all + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the current and finite {@code Observable}, in a + * sorted order. Each item emitted by the current {@code Observable} must implement {@link Comparable} with respect to all * other items in the sequence. * - *

If any item emitted by this Observable does not implement {@link Comparable} with respect to - * all other items emitted by this Observable, no items will be emitted and the - * sequence is terminated with a {@link ClassCastException}. *

- * + * If any item emitted by the current {@code Observable} does not implement {@code Comparable} with respect to + * all other items emitted by the current {@code Observable}, no items will be emitted and the + * sequence is terminated with a {@link ClassCastException}. *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to + * + *

+ * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
- * @return a Single that emits a list that contains the items emitted by the source ObservableSource in - * sorted order + * @return the new {@code Single} instance * @see ReactiveX operators documentation: To + * @see #toSortedList(int) + * @see #toSortedList(Comparator) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toSortedList() { - return toSortedList(Functions.naturalOrder()); + @NonNull + public final Single<@NonNull List> toSortedList() { + return toSortedList(Functions.naturalComparator()); } /** - * Returns a Single that emits a list that contains the items emitted by the finite source ObservableSource, in a + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the current and finite {@code Observable}, in a * sorted order based on a specified comparison function. *

- * + * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
* * @param comparator - * a function that compares two items emitted by the source ObservableSource and returns an Integer + * a function that compares two items emitted by the current {@code Observable} and returns an {@code int} * that indicates their sort order - * @return a Single that emits a list that contains the items emitted by the source ObservableSource in - * sorted order + * @return the new {@code Single} instance + * @throws NullPointerException if {@code comparator} is {@code null} * @see ReactiveX operators documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toSortedList(final Comparator comparator) { - ObjectHelper.requireNonNull(comparator, "comparator is null"); + @NonNull + public final Single<@NonNull List> toSortedList(@NonNull Comparator comparator) { + Objects.requireNonNull(comparator, "comparator is null"); return toList().map(Functions.listSorter(comparator)); } /** - * Returns a Single that emits a list that contains the items emitted by the finite source ObservableSource, in a + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the current and finite {@code Observable}, in a * sorted order based on a specified comparison function. *

- * + * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
* * @param comparator - * a function that compares two items emitted by the source ObservableSource and returns an Integer + * a function that compares two items emitted by the current {@code Observable} and returns an {@code int} * that indicates their sort order * @param capacityHint - * the initial capacity of the ArrayList used to accumulate items before sorting - * @return a Single that emits a list that contains the items emitted by the source ObservableSource in - * sorted order + * the initial capacity of the {@code List} used to accumulate items before sorting + * @return the new {@code Single} instance + * @throws NullPointerException if {@code comparator} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX operators documentation: To * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toSortedList(final Comparator comparator, int capacityHint) { - ObjectHelper.requireNonNull(comparator, "comparator is null"); + @NonNull + public final Single<@NonNull List> toSortedList(@NonNull Comparator comparator, int capacityHint) { + Objects.requireNonNull(comparator, "comparator is null"); return toList(capacityHint).map(Functions.listSorter(comparator)); } /** - * Returns a Single that emits a list that contains the items emitted by the finite source ObservableSource, in a - * sorted order. Each item emitted by the ObservableSource must implement {@link Comparable} with respect to all + * Returns a {@link Single} that emits a {@link List} that contains the items emitted by the current and finite {@code Observable}, in a + * sorted order. Each item emitted by the current {@code Observable} must implement {@link Comparable} with respect to all * other items in the sequence. - * - *

If any item emitted by this Observable does not implement {@link Comparable} with respect to - * all other items emitted by this Observable, no items will be emitted and the - * sequence is terminated with a {@link ClassCastException}. *

- * + * If any item emitted by the current {@code Observable} does not implement {@code Comparable} with respect to + * all other items emitted by the current {@code Observable}, no items will be emitted and the + * sequence is terminated with a {@link ClassCastException}. + *

+ * *

- * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated list to + * Note that this operator requires the upstream to signal {@code onComplete} for the accumulated {@code List} to * be emitted. Sources that are infinite and never complete will never emit anything through this - * operator and an infinite source may lead to a fatal {@code OutOfMemoryError}. + * operator and an infinite source may lead to a fatal {@link OutOfMemoryError}. *

*
Scheduler:
*
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
*
* * @param capacityHint - * the initial capacity of the ArrayList used to accumulate items before sorting - * @return a Single that emits a list that contains the items emitted by the source ObservableSource in - * sorted order + * the initial capacity of the {@code List} used to accumulate items before sorting + * @return the new {@code Single} instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @see ReactiveX operators documentation: To * @since 2.0 + * @see #toSortedList(Comparator, int) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single> toSortedList(int capacityHint) { - return toSortedList(Functions.naturalOrder(), capacityHint); + @NonNull + public final Single<@NonNull List> toSortedList(int capacityHint) { + return toSortedList(Functions.naturalComparator(), capacityHint); } /** - * Modifies the source ObservableSource so that subscribers will dispose it on a specified - * {@link Scheduler}. + * Return an {@code Observable} that schedules the downstream {@link Observer}s' {@code dispose} calls + * aimed at the current {@code Observable} on the given {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to perform the call to dispose() of the upstream Disposable - * @return the source ObservableSource modified so that its dispose() calls happen on the specified - * {@link Scheduler} + * the {@code Scheduler} to perform the call to {@code dispose()} of the upstream {@link Disposable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable unsubscribeOn(Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableUnsubscribeOn(this, scheduler)); + @NonNull + public final Observable unsubscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableUnsubscribeOn<>(this, scheduler)); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each containing {@code count} items. When the source - * ObservableSource completes or encounters an error, the resulting ObservableSource emits the current window and - * propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each containing {@code count} items. When the current + * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the current window and + * propagates the notification from the current {@code Observable}. *

- * + * *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -14456,24 +15750,24 @@ public final Observable unsubscribeOn(Scheduler scheduler) { * * @param count * the maximum size of each window before it should be emitted - * @return an Observable that emits connected, non-overlapping windows, each containing at most - * {@code count} items from the source ObservableSource - * @throws IllegalArgumentException if either count is non-positive + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable> window(long count) { return window(count, count, bufferSize()); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits windows every {@code skip} items, each containing no more than {@code count} items. When - * the source ObservableSource completes or encounters an error, the resulting ObservableSource emits the current window - * and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits windows every {@code skip} items, each containing no more than {@code count} items. When + * the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the current window + * and propagates the notification from the current {@code Observable}. *

- * + * *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -14484,24 +15778,24 @@ public final Observable> window(long count) { * @param skip * how many items need to be skipped before starting a new window. Note that if {@code skip} and * {@code count} are equal this is the same operation as {@link #window(long)}. - * @return an Observable that emits windows every {@code skip} items containing at most {@code count} items - * from the source ObservableSource - * @throws IllegalArgumentException if either count or skip is non-positive + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code count} or {@code skip} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable> window(long count, long skip) { return window(count, skip, bufferSize()); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits windows every {@code skip} items, each containing no more than {@code count} items. When - * the source ObservableSource completes or encounters an error, the resulting ObservableSource emits the current window - * and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits windows every {@code skip} items, each containing no more than {@code count} items. When + * the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the current window + * and propagates the notification from the current {@code Observable}. *

- * + * *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -14514,28 +15808,33 @@ public final Observable> window(long count, long skip) { * {@code count} are equal this is the same operation as {@link #window(long)}. * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return an Observable that emits windows every {@code skip} items containing at most {@code count} items - * from the source ObservableSource - * @throws IllegalArgumentException if either count or skip is non-positive + * @return the new {@code Observable} instance + * @throws IllegalArgumentException if {@code count}, {@code skip} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Observable> window(long count, long skip, int bufferSize) { ObjectHelper.verifyPositive(count, "count"); ObjectHelper.verifyPositive(skip, "skip"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableWindow(this, count, skip, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableWindow<>(this, count, skip, bufferSize)); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource starts a new window periodically, as determined by the {@code timeskip} argument. It emits - * each window after a fixed timespan, specified by the {@code timespan} argument. When the source - * ObservableSource completes or ObservableSource completes or encounters an error, the resulting ObservableSource emits the - * current window and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} starts a new window periodically, as determined by the {@code timeskip} argument. It emits + * each window after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the + * current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -14547,23 +15846,31 @@ public final Observable> window(long count, long skip, int bufferS * the period of time after which a new window will be created * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments - * @return an Observable that emits new windows periodically as a fixed timespan elapses + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code timespan} or {@code timeskip} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> window(long timespan, long timeskip, TimeUnit unit) { + @NonNull + public final Observable> window(long timespan, long timeskip, @NonNull TimeUnit unit) { return window(timespan, timeskip, unit, Schedulers.computation(), bufferSize()); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource starts a new window periodically, as determined by the {@code timeskip} argument. It emits - * each window after a fixed timespan, specified by the {@code timespan} argument. When the source - * ObservableSource completes or ObservableSource completes or encounters an error, the resulting ObservableSource emits the - * current window and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} starts a new window periodically, as determined by the {@code timeskip} argument. It emits + * each window after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the + * current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -14576,24 +15883,32 @@ public final Observable> window(long timespan, long timeskip, Time * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window - * @return an Observable that emits new windows periodically as a fixed timespan elapses + * the {@code Scheduler} to use when determining the end and start of a window + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code timespan} or {@code timeskip} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> window(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable> window(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return window(timespan, timeskip, unit, scheduler, bufferSize()); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource starts a new window periodically, as determined by the {@code timeskip} argument. It emits - * each window after a fixed timespan, specified by the {@code timespan} argument. When the source - * ObservableSource completes or ObservableSource completes or encounters an error, the resulting ObservableSource emits the - * current window and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} starts a new window periodically, as determined by the {@code timeskip} argument. It emits + * each window after a fixed timespan, specified by the {@code timespan} argument. When the current + * {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the + * current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -14606,30 +15921,38 @@ public final Observable> window(long timespan, long timeskip, Time * @param unit * the unit of time that applies to the {@code timespan} and {@code timeskip} arguments * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window + * the {@code Scheduler} to use when determining the end and start of a window * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return an Observable that emits new windows periodically as a fixed timespan elapses + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code timespan}, {@code timeskip} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> window(long timespan, long timeskip, TimeUnit unit, Scheduler scheduler, int bufferSize) { + @NonNull + public final Observable> window(long timespan, long timeskip, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, int bufferSize) { ObjectHelper.verifyPositive(timespan, "timespan"); ObjectHelper.verifyPositive(timeskip, "timeskip"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); - return RxJavaPlugins.onAssembly(new ObservableWindowTimed(this, timespan, timeskip, unit, scheduler, Long.MAX_VALUE, bufferSize, false)); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); + return RxJavaPlugins.onAssembly(new ObservableWindowTimed<>(this, timespan, timeskip, unit, scheduler, Long.MAX_VALUE, bufferSize, false)); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration specified by the - * {@code timespan} argument. When the source ObservableSource completes or encounters an error, the resulting - * ObservableSource emits the current window and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration specified by the + * {@code timespan} argument. When the current {@code Observable} completes or encounters an error, the resulting + * {@code Observable} emits the current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -14640,24 +15963,30 @@ public final Observable> window(long timespan, long timeskip, Time * new window * @param unit * the unit of time that applies to the {@code timespan} argument - * @return an Observable that emits connected, non-overlapping windows representing items emitted by the - * source ObservableSource during fixed, consecutive durations + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> window(long timespan, TimeUnit unit) { + @NonNull + public final Observable> window(long timespan, @NonNull TimeUnit unit) { return window(timespan, unit, Schedulers.computation(), Long.MAX_VALUE, false); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration as specified by the + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration as specified by the * {@code timespan} argument or a maximum size as specified by the {@code count} argument (whichever is - * reached first). When the source ObservableSource completes or encounters an error, the resulting ObservableSource - * emits the current window and propagates the notification from the source ObservableSource. + * reached first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} + * emits the current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -14670,26 +15999,32 @@ public final Observable> window(long timespan, TimeUnit unit) { * the unit of time that applies to the {@code timespan} argument * @param count * the maximum size of each window before it should be emitted - * @return an Observable that emits connected, non-overlapping windows of items from the source ObservableSource - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> window(long timespan, TimeUnit unit, + @NonNull + public final Observable> window(long timespan, @NonNull TimeUnit unit, long count) { return window(timespan, unit, Schedulers.computation(), count, false); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration as specified by the + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration as specified by the * {@code timespan} argument or a maximum size as specified by the {@code count} argument (whichever is - * reached first). When the source ObservableSource completes or encounters an error, the resulting ObservableSource - * emits the current window and propagates the notification from the source ObservableSource. + * reached first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} + * emits the current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} operates by default on the {@code computation} {@link Scheduler}.
@@ -14703,26 +16038,32 @@ public final Observable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param restart - * if true, when a window reaches the capacity limit, the timer is restarted as well - * @return an Observable that emits connected, non-overlapping windows of items from the source ObservableSource - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * if {@code true}, when a window reaches the capacity limit, the timer is restarted as well + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable> window(long timespan, TimeUnit unit, + @NonNull + public final Observable> window(long timespan, @NonNull TimeUnit unit, long count, boolean restart) { return window(timespan, unit, Schedulers.computation(), count, restart); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration as specified by the - * {@code timespan} argument. When the source ObservableSource completes or encounters an error, the resulting - * ObservableSource emits the current window and propagates the notification from the source ObservableSource. + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration as specified by the + * {@code timespan} argument. When the current {@code Observable} completes or encounters an error, the resulting + * {@code Observable} emits the current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -14734,26 +16075,32 @@ public final Observable> window(long timespan, TimeUnit unit, * @param unit * the unit of time which applies to the {@code timespan} argument * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window - * @return an Observable that emits connected, non-overlapping windows containing items emitted by the - * source ObservableSource within a fixed duration + * the {@code Scheduler} to use when determining the end and start of a window + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> window(long timespan, TimeUnit unit, - Scheduler scheduler) { + @NonNull + public final Observable> window(long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler) { return window(timespan, unit, scheduler, Long.MAX_VALUE, false); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration specified by the + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source ObservableSource completes or encounters an error, the resulting ObservableSource emits the - * current window and propagates the notification from the source ObservableSource. + * first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the + * current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -14767,27 +16114,33 @@ public final Observable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window - * @return an Observable that emits connected, non-overlapping windows of items from the source ObservableSource - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * the {@code Scheduler} to use when determining the end and start of a window + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> window(long timespan, TimeUnit unit, - Scheduler scheduler, long count) { + @NonNull + public final Observable> window(long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, long count) { return window(timespan, unit, scheduler, count, false); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration specified by the + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source ObservableSource completes or encounters an error, the resulting ObservableSource emits the - * current window and propagates the notification from the source ObservableSource. + * first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the + * current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -14801,29 +16154,35 @@ public final Observable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window + * the {@code Scheduler} to use when determining the end and start of a window * @param restart - * if true, when a window reaches the capacity limit, the timer is restarted as well - * @return an Observable that emits connected, non-overlapping windows of items from the source ObservableSource - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * if {@code true}, when a window reaches the capacity limit, the timer is restarted as well + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable> window(long timespan, TimeUnit unit, - Scheduler scheduler, long count, boolean restart) { + @NonNull + public final Observable> window(long timespan, @NonNull TimeUnit unit, + @NonNull Scheduler scheduler, long count, boolean restart) { return window(timespan, unit, scheduler, count, restart, bufferSize()); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits connected, non-overlapping windows, each of a fixed duration specified by the + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits connected, non-overlapping windows, each of a fixed duration specified by the * {@code timespan} argument or a maximum size specified by the {@code count} argument (whichever is reached - * first). When the source ObservableSource completes or encounters an error, the resulting ObservableSource emits the - * current window and propagates the notification from the source ObservableSource. + * first). When the current {@code Observable} completes or encounters an error, the resulting {@code Observable} emits the + * current window and propagates the notification from the current {@code Observable}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
You specify which {@link Scheduler} this operator will use.
@@ -14837,34 +16196,40 @@ public final Observable> window(long timespan, TimeUnit unit, * @param count * the maximum size of each window before it should be emitted * @param scheduler - * the {@link Scheduler} to use when determining the end and start of a window + * the {@code Scheduler} to use when determining the end and start of a window * @param restart - * if true, when a window reaches the capacity limit, the timer is restarted as well + * if {@code true}, when a window reaches the capacity limit, the timer is restarted as well * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return an Observable that emits connected, non-overlapping windows of items from the source ObservableSource - * that were emitted during a fixed duration of time or when the window has reached maximum capacity - * (whichever occurs first) + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code count} or {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) + @NonNull public final Observable> window( - long timespan, TimeUnit unit, Scheduler scheduler, + long timespan, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, long count, boolean restart, int bufferSize) { ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - ObjectHelper.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + Objects.requireNonNull(unit, "unit is null"); ObjectHelper.verifyPositive(count, "count"); - return RxJavaPlugins.onAssembly(new ObservableWindowTimed(this, timespan, timespan, unit, scheduler, count, bufferSize, restart)); + return RxJavaPlugins.onAssembly(new ObservableWindowTimed<>(this, timespan, timespan, unit, scheduler, count, bufferSize, restart)); } /** - * Returns an Observable that emits non-overlapping windows of items it collects from the source ObservableSource + * Returns an {@code Observable} that emits non-overlapping windows of items it collects from the current {@code Observable} * where the boundary of each window is determined by the items emitted from a specified boundary-governing - * ObservableSource. + * {@link ObservableSource}. + *

+ * *

- * + * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -14872,25 +16237,30 @@ public final Observable> window( * * @param * the window element type (ignored) - * @param boundary - * an ObservableSource whose emitted items close and open windows - * @return an Observable that emits non-overlapping windows of items it collects from the source ObservableSource - * where the boundary of each window is determined by the items emitted from the {@code boundary} - * ObservableSource + * @param boundaryIndicator + * an {@code ObservableSource} whose emitted items close and open windows + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> window(ObservableSource boundary) { - return window(boundary, bufferSize()); + @NonNull + public final <@NonNull B> Observable> window(@NonNull ObservableSource boundaryIndicator) { + return window(boundaryIndicator, bufferSize()); } /** - * Returns an Observable that emits non-overlapping windows of items it collects from the source ObservableSource + * Returns an {@code Observable} that emits non-overlapping windows of items it collects from the current {@code Observable} * where the boundary of each window is determined by the items emitted from a specified boundary-governing - * ObservableSource. + * {@link ObservableSource}. *

- * + * + *

+ * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
@@ -14898,133 +16268,156 @@ public final Observable> window(ObservableSource boundary) * * @param * the window element type (ignored) - * @param boundary - * an ObservableSource whose emitted items close and open windows + * @param boundaryIndicator + * an {@code ObservableSource} whose emitted items close and open windows * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return an Observable that emits non-overlapping windows of items it collects from the source ObservableSource - * where the boundary of each window is determined by the items emitted from the {@code boundary} - * ObservableSource + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code boundaryIndicator} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> window(ObservableSource boundary, int bufferSize) { - ObjectHelper.requireNonNull(boundary, "boundary is null"); + @NonNull + public final <@NonNull B> Observable> window(@NonNull ObservableSource boundaryIndicator, int bufferSize) { + Objects.requireNonNull(boundaryIndicator, "boundaryIndicator is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableWindowBoundary(this, boundary, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableWindowBoundary<>(this, boundaryIndicator, bufferSize)); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits windows that contain those items emitted by the source ObservableSource between the time when - * the {@code openingIndicator} ObservableSource emits an item and when the ObservableSource returned by + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits windows that contain those items emitted by the current {@code Observable} between the time when + * the {@code openingIndicator} {@link ObservableSource} emits an item and when the {@code ObservableSource} returned by * {@code closingIndicator} emits an item. *

- * + * + *

+ * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the window-opening ObservableSource - * @param the element type of the window-closing ObservableSources + * @param the element type of the window-opening {@code ObservableSource} + * @param the element type of the window-closing {@code ObservableSource}s * @param openingIndicator - * an ObservableSource that, when it emits an item, causes another window to be created + * an {@code ObservableSource} that, when it emits an item, causes another window to be created * @param closingIndicator - * a {@link Function} that produces an ObservableSource for every window created. When this ObservableSource - * emits an item, the associated window is closed and emitted - * @return an Observable that emits windows of items emitted by the source ObservableSource that are governed by - * the specified window-governing ObservableSources + * a {@link Function} that produces an {@code ObservableSource} for every window created. When this indicator {@code ObservableSource} + * emits an item, the associated window is completed + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code openingIndicator} or {@code closingIndicator} is {@code null} * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> window( - ObservableSource openingIndicator, - Function> closingIndicator) { + @NonNull + public final <@NonNull U, @NonNull V> Observable> window( + @NonNull ObservableSource openingIndicator, + @NonNull Function> closingIndicator) { return window(openingIndicator, closingIndicator, bufferSize()); } /** - * Returns an Observable that emits windows of items it collects from the source ObservableSource. The resulting - * ObservableSource emits windows that contain those items emitted by the source ObservableSource between the time when - * the {@code openingIndicator} ObservableSource emits an item and when the ObservableSource returned by + * Returns an {@code Observable} that emits windows of items it collects from the current {@code Observable}. The resulting + * {@code Observable} emits windows that contain those items emitted by the current {@code Observable} between the time when + * the {@code openingIndicator} {@link ObservableSource} emits an item and when the {@code ObservableSource} returned by * {@code closingIndicator} emits an item. *

- * + * + *

+ * Note that ignoring windows or subscribing later (i.e., on another thread) will result in + * so-called window abandonment where a window may not contain any elements. In this case, subsequent + * elements will be dropped until the condition for the next window boundary is satisfied. The behavior is + * a trade-off for ensuring upstream cancellation can happen under some race conditions. *

*
Scheduler:
*
This version of {@code window} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the element type of the window-opening ObservableSource - * @param the element type of the window-closing ObservableSources + * @param the element type of the window-opening {@code ObservableSource} + * @param the element type of the window-closing {@code ObservableSource}s * @param openingIndicator - * an ObservableSource that, when it emits an item, causes another window to be created + * an {@code ObservableSource} that, when it emits an item, causes another window to be created * @param closingIndicator - * a {@link Function} that produces an ObservableSource for every window created. When this ObservableSource - * emits an item, the associated window is closed and emitted + * a {@link Function} that produces an {@code ObservableSource} for every window created. When this indicator {@code ObservableSource} + * emits an item, the associated window is completed * @param bufferSize * the capacity hint for the buffer in the inner windows - * @return an Observable that emits windows of items emitted by the source ObservableSource that are governed by - * the specified window-governing ObservableSources + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code openingIndicator} or {@code closingIndicator} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @see ReactiveX operators documentation: Window */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable> window( - ObservableSource openingIndicator, - Function> closingIndicator, int bufferSize) { - ObjectHelper.requireNonNull(openingIndicator, "openingIndicator is null"); - ObjectHelper.requireNonNull(closingIndicator, "closingIndicator is null"); + @NonNull + public final <@NonNull U, @NonNull V> Observable> window( + @NonNull ObservableSource openingIndicator, + @NonNull Function> closingIndicator, int bufferSize) { + Objects.requireNonNull(openingIndicator, "openingIndicator is null"); + Objects.requireNonNull(closingIndicator, "closingIndicator is null"); ObjectHelper.verifyPositive(bufferSize, "bufferSize"); - return RxJavaPlugins.onAssembly(new ObservableWindowBoundarySelector(this, openingIndicator, closingIndicator, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableWindowBoundarySelector<>(this, openingIndicator, closingIndicator, bufferSize)); } /** - * Merges the specified ObservableSource into this ObservableSource sequence by using the {@code resultSelector} - * function only when the source ObservableSource (this instance) emits an item. + * Merges the specified {@link ObservableSource} into the current {@code Observable} sequence by using the {@code resultSelector} + * function only when the current {@code Observable} emits an item. + * + *

Note that this operator doesn't emit anything until the other source has produced at + * least one value. The resulting emission only happens when the current {@code Observable} emits (and + * not when the other source emits, unlike combineLatest). + * If the other source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before the other source has produced at least one value, the sequence completes + * without emission. *

- * + * * *

*
Scheduler:
*
This operator, by default, doesn't run any particular {@link Scheduler}.
*
* - * @param the element type of the other ObservableSource + * @param the element type of the other {@code ObservableSource} * @param the result type of the combination * @param other - * the other ObservableSource + * the other {@code ObservableSource} * @param combiner - * the function to call when this ObservableSource emits an item and the other ObservableSource has already - * emitted an item, to generate the item to be emitted by the resulting ObservableSource - * @return an Observable that merges the specified ObservableSource into this ObservableSource by using the - * {@code resultSelector} function only when the source ObservableSource sequence (this instance) emits an - * item + * the function to call when the current {@code Observable} emits an item and the other {@code ObservableSource} has already + * emitted an item, to generate the item to be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} or {@code combiner} is {@code null} * @since 2.0 * @see ReactiveX operators documentation: CombineLatest */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable withLatestFrom(ObservableSource other, BiFunction combiner) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + @NonNull + public final <@NonNull U, @NonNull R> Observable withLatestFrom(@NonNull ObservableSource other, @NonNull BiFunction combiner) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(combiner, "combiner is null"); return RxJavaPlugins.onAssembly(new ObservableWithLatestFrom(this, combiner, other)); } /** - * Combines the value emission from this ObservableSource with the latest emissions from the - * other ObservableSources via a function to produce the output item. + * Combines the value emission from the current {@code Observable} with the latest emissions from the + * other {@link ObservableSource}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this ObservableSource emits (and - * not when any of the other sources emit, unlike combineLatest). + * least one value. The resulting emission only happens when the current {@code Observable} emits (and + * not when any of the other sources emit, unlike {@code combineLatest}). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -15033,34 +16426,38 @@ public final Observable withLatestFrom(ObservableSource o * @param the first other source's value type * @param the second other source's value type * @param the result value type - * @param o1 the first other ObservableSource - * @param o2 the second other ObservableSource - * @param combiner the function called with an array of values from each participating ObservableSource - * @return the new ObservableSource instance + * @param source1 the first other {@code ObservableSource} + * @param source2 the second other {@code ObservableSource} + * @param combiner the function called with an array of values from each participating {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable withLatestFrom( - ObservableSource o1, ObservableSource o2, - Function3 combiner) { - ObjectHelper.requireNonNull(o1, "o1 is null"); - ObjectHelper.requireNonNull(o2, "o2 is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + @NonNull + public final <@NonNull T1, @NonNull T2, @NonNull R> Observable withLatestFrom( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull Function3 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(combiner, "combiner is null"); Function f = Functions.toFunction(combiner); - return withLatestFrom(new ObservableSource[] { o1, o2 }, f); + return withLatestFrom(new ObservableSource[] { source1, source2 }, f); } /** - * Combines the value emission from this ObservableSource with the latest emissions from the - * other ObservableSources via a function to produce the output item. + * Combines the value emission from the current {@code Observable} with the latest emissions from the + * other {@link ObservableSource}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this ObservableSource emits (and + * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -15070,37 +16467,41 @@ public final Observable withLatestFrom( * @param the second other source's value type * @param the third other source's value type * @param the result value type - * @param o1 the first other ObservableSource - * @param o2 the second other ObservableSource - * @param o3 the third other ObservableSource - * @param combiner the function called with an array of values from each participating ObservableSource - * @return the new ObservableSource instance + * @param source1 the first other {@code ObservableSource} + * @param source2 the second other {@code ObservableSource} + * @param source3 the third other {@code ObservableSource} + * @param combiner the function called with an array of values from each participating {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable withLatestFrom( - ObservableSource o1, ObservableSource o2, - ObservableSource o3, - Function4 combiner) { - ObjectHelper.requireNonNull(o1, "o1 is null"); - ObjectHelper.requireNonNull(o2, "o2 is null"); - ObjectHelper.requireNonNull(o3, "o3 is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + @NonNull + public final <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Observable withLatestFrom( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, + @NonNull Function4 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(combiner, "combiner is null"); Function f = Functions.toFunction(combiner); - return withLatestFrom(new ObservableSource[] { o1, o2, o3 }, f); + return withLatestFrom(new ObservableSource[] { source1, source2, source3 }, f); } /** - * Combines the value emission from this ObservableSource with the latest emissions from the - * other ObservableSources via a function to produce the output item. + * Combines the value emission from the current {@code Observable} with the latest emissions from the + * other {@link ObservableSource}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this ObservableSource emits (and + * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -15111,39 +16512,44 @@ public final Observable withLatestFrom( * @param the third other source's value type * @param the fourth other source's value type * @param the result value type - * @param o1 the first other ObservableSource - * @param o2 the second other ObservableSource - * @param o3 the third other ObservableSource - * @param o4 the fourth other ObservableSource - * @param combiner the function called with an array of values from each participating ObservableSource - * @return the new ObservableSource instance + * @param source1 the first other {@code ObservableSource} + * @param source2 the second other {@code ObservableSource} + * @param source3 the third other {@code ObservableSource} + * @param source4 the fourth other {@code ObservableSource} + * @param combiner the function called with an array of values from each participating {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, + * {@code source4} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable withLatestFrom( - ObservableSource o1, ObservableSource o2, - ObservableSource o3, ObservableSource o4, - Function5 combiner) { - ObjectHelper.requireNonNull(o1, "o1 is null"); - ObjectHelper.requireNonNull(o2, "o2 is null"); - ObjectHelper.requireNonNull(o3, "o3 is null"); - ObjectHelper.requireNonNull(o4, "o4 is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); + @NonNull + public final <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Observable withLatestFrom( + @NonNull ObservableSource source1, @NonNull ObservableSource source2, + @NonNull ObservableSource source3, @NonNull ObservableSource source4, + @NonNull Function5 combiner) { + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(combiner, "combiner is null"); Function f = Functions.toFunction(combiner); - return withLatestFrom(new ObservableSource[] { o1, o2, o3, o4 }, f); + return withLatestFrom(new ObservableSource[] { source1, source2, source3, source4 }, f); } /** - * Combines the value emission from this ObservableSource with the latest emissions from the - * other ObservableSources via a function to produce the output item. + * Combines the value emission from the current {@code Observable} with the latest emissions from the + * other {@link ObservableSource}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this ObservableSource emits (and + * least one value. The resulting emission only happens when the current {@code Observable} emits (and * not when any of the other sources emit, unlike combineLatest). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -15151,28 +16557,32 @@ public final Observable withLatestFrom( * * @param the result value type * @param others the array of other sources - * @param combiner the function called with an array of values from each participating ObservableSource - * @return the new ObservableSource instance + * @param combiner the function called with an array of values from each participating {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code others} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable withLatestFrom(ObservableSource[] others, Function combiner) { - ObjectHelper.requireNonNull(others, "others is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); - return RxJavaPlugins.onAssembly(new ObservableWithLatestFromMany(this, others, combiner)); + @NonNull + public final <@NonNull R> Observable withLatestFrom(@NonNull ObservableSource[] others, @NonNull Function combiner) { + Objects.requireNonNull(others, "others is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return RxJavaPlugins.onAssembly(new ObservableWithLatestFromMany<>(this, others, combiner)); } /** - * Combines the value emission from this ObservableSource with the latest emissions from the - * other ObservableSources via a function to produce the output item. + * Combines the value emission from the current {@code Observable} with the latest emissions from the + * other {@link ObservableSource}s via a function to produce the output item. * *

Note that this operator doesn't emit anything until all other sources have produced at - * least one value. The resulting emission only happens when this ObservableSource emits (and - * not when any of the other sources emit, unlike combineLatest). + * least one value. The resulting emission only happens when the current {@code Observable} emits (and + * not when any of the other sources emit, unlike {@code combineLatest}). * If a source doesn't produce any value and just completes, the sequence is completed immediately. + * If the upstream completes before all other sources have produced at least one value, the sequence completes + * without emission. *

- * + * *

*
Scheduler:
*
This operator does not operate by default on a particular {@link Scheduler}.
@@ -15180,25 +16590,27 @@ public final Observable withLatestFrom(ObservableSource[] others, Func * * @param the result value type * @param others the iterable of other sources - * @param combiner the function called with an array of values from each participating ObservableSource - * @return the new ObservableSource instance + * @param combiner the function called with an array of values from each participating {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code others} or {@code combiner} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable withLatestFrom(Iterable> others, Function combiner) { - ObjectHelper.requireNonNull(others, "others is null"); - ObjectHelper.requireNonNull(combiner, "combiner is null"); - return RxJavaPlugins.onAssembly(new ObservableWithLatestFromMany(this, others, combiner)); + @NonNull + public final <@NonNull R> Observable withLatestFrom(@NonNull Iterable<@NonNull ? extends ObservableSource> others, @NonNull Function combiner) { + Objects.requireNonNull(others, "others is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return RxJavaPlugins.onAssembly(new ObservableWithLatestFromMany<>(this, others, combiner)); } /** - * Returns an Observable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source ObservableSource and a specified Iterable sequence. + * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Observable} and a specified {@link Iterable} sequence. *

- * + * *

- * Note that the {@code other} Iterable is evaluated as items are observed from the source ObservableSource; it is + * Note that the {@code other} {@code Iterable} is evaluated as items are observed from the current {@code Observable}; it is * not pre-consumed. This allows you to zip infinite streams on either side. *

*
Scheduler:
@@ -15206,31 +16618,32 @@ public final Observable withLatestFrom(Iterable * * @param - * the type of items in the {@code other} Iterable + * the type of items in the {@code other} {@code Iterable} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param other - * the Iterable sequence + * the {@code Iterable} sequence * @param zipper - * a function that combines the pairs of items from the ObservableSource and the Iterable to generate - * the items to be emitted by the resulting ObservableSource - * @return an Observable that pairs up values from the source ObservableSource and the {@code other} Iterable - * sequence and emits the results of {@code zipFunction} applied to these pairs + * a function that combines the pairs of items from the current {@code Observable} and the {@code Iterable} to generate + * the items to be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable zipWith(Iterable other, BiFunction zipper) { - ObjectHelper.requireNonNull(other, "other is null"); - ObjectHelper.requireNonNull(zipper, "zipper is null"); - return RxJavaPlugins.onAssembly(new ObservableZipIterable(this, other, zipper)); + @NonNull + public final <@NonNull U, @NonNull R> Observable zipWith(@NonNull Iterable other, @NonNull BiFunction zipper) { + Objects.requireNonNull(other, "other is null"); + Objects.requireNonNull(zipper, "zipper is null"); + return RxJavaPlugins.onAssembly(new ObservableZipIterable<>(this, other, zipper)); } /** - * Returns an Observable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source ObservableSource and another specified ObservableSource. + * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Observable} and another specified {@link ObservableSource}. *

- * + * *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -15249,31 +16662,32 @@ public final Observable zipWith(Iterable other, BiFunction * * @param - * the type of items emitted by the {@code other} ObservableSource + * the type of items emitted by the {@code other} {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param other - * the other ObservableSource + * the other {@code ObservableSource} * @param zipper - * a function that combines the pairs of items from the two ObservableSources to generate the items to - * be emitted by the resulting ObservableSource - * @return an Observable that pairs up values from the source ObservableSource and the {@code other} ObservableSource - * and emits the results of {@code zipFunction} applied to these pairs + * a function that combines the pairs of items from the current {@code Observable} and the other {@code ObservableSource} to generate the items to + * be emitted by the resulting {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable zipWith(ObservableSource other, - BiFunction zipper) { - ObjectHelper.requireNonNull(other, "other is null"); + @NonNull + public final <@NonNull U, @NonNull R> Observable zipWith(@NonNull ObservableSource other, + @NonNull BiFunction zipper) { + Objects.requireNonNull(other, "other is null"); return zip(this, other, zipper); } /** - * Returns an Observable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source ObservableSource and another specified ObservableSource. + * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Observable} and another specified {@link ObservableSource}. *

- * + * *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -15292,33 +16706,34 @@ public final Observable zipWith(ObservableSource other, *

* * @param - * the type of items emitted by the {@code other} ObservableSource + * the type of items emitted by the {@code other} {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param other - * the other ObservableSource + * the other {@code ObservableSource} * @param zipper - * a function that combines the pairs of items from the two ObservableSources to generate the items to - * be emitted by the resulting ObservableSource + * a function that combines the pairs of items from the current {@code Observable} and the other {@code ObservableSource} to generate the items to + * be emitted by the resulting {@code Observable} * @param delayError - * if true, errors from the current Observable or the other ObservableSource is delayed until both terminate - * @return an Observable that pairs up values from the source ObservableSource and the {@code other} ObservableSource - * and emits the results of {@code zipFunction} applied to these pairs + * if {@code true}, errors from the current {@code Observable} or the other {@code ObservableSource} is delayed until both terminate + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable zipWith(ObservableSource other, - BiFunction zipper, boolean delayError) { + @NonNull + public final <@NonNull U, @NonNull R> Observable zipWith(@NonNull ObservableSource other, + @NonNull BiFunction zipper, boolean delayError) { return zip(this, other, zipper, delayError); } /** - * Returns an Observable that emits items that are the result of applying a specified function to pairs of - * values, one each from the source ObservableSource and another specified ObservableSource. + * Returns an {@code Observable} that emits items that are the result of applying a specified function to pairs of + * values, one each from the current {@code Observable} and another specified {@link ObservableSource}. *

- * + * *

* The operator subscribes to its sources in order they are specified and completes eagerly if * one of the sources is shorter than the rest while disposing the other sources. Therefore, it @@ -15337,27 +16752,29 @@ public final Observable zipWith(ObservableSource other, *

* * @param - * the type of items emitted by the {@code other} ObservableSource + * the type of items emitted by the {@code other} {@code ObservableSource} * @param - * the type of items emitted by the resulting ObservableSource + * the type of items emitted by the resulting {@code Observable} * @param other - * the other ObservableSource + * the other {@code ObservableSource} * @param zipper - * a function that combines the pairs of items from the two ObservableSources to generate the items to - * be emitted by the resulting ObservableSource + * a function that combines the pairs of items from the current {@code Observable} and the other {@code ObservableSource} to generate the items to + * be emitted by the resulting {@code Observable} * @param bufferSize * the capacity hint for the buffer in the inner windows * @param delayError - * if true, errors from the current Observable or the other ObservableSource is delayed until both terminate - * @return an Observable that pairs up values from the source ObservableSource and the {@code other} ObservableSource - * and emits the results of {@code zipFunction} applied to these pairs + * if {@code true}, errors from the current {@code Observable} or the other {@code ObservableSource} is delayed until both terminate + * @return the new {@code Observable} instance * @see ReactiveX operators documentation: Zip + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable zipWith(ObservableSource other, - BiFunction zipper, boolean delayError, int bufferSize) { + @NonNull + public final <@NonNull U, @NonNull R> Observable zipWith(@NonNull ObservableSource other, + @NonNull BiFunction zipper, boolean delayError, int bufferSize) { return zip(this, other, zipper, delayError, bufferSize); } @@ -15365,43 +16782,557 @@ public final Observable zipWith(ObservableSource other, // Fluent test support, super handy and reduces test preparation boilerplate // ------------------------------------------------------------------------- /** - * Creates a TestObserver and subscribes - * it to this Observable. + * Creates a {@link TestObserver} and subscribes it to the current {@code Observable}. *
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new TestObserver instance + * @return the new {@code TestObserver} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test() { // NoPMD - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); subscribe(to); return to; } /** - * Creates a TestObserver, optionally disposes it and then subscribes - * it to this Observable. + * Creates a {@link TestObserver}, optionally disposes it and then subscribes + * it to the current {@code Observable}. * *
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @param dispose dispose the TestObserver before it is subscribed to this Observable? - * @return the new TestObserver instance + * @param dispose indicates if the {@code TestObserver} should be disposed before + * it is subscribed to the current {@code Observable} + * @return the new {@code TestObserver} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test(boolean dispose) { // NoPMD - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); if (dispose) { to.dispose(); } subscribe(to); return to; } + + // ------------------------------------------------------------------------- + // JDK 8 Support + // ------------------------------------------------------------------------- + + /** + * Converts the existing value of the provided optional into a {@link #just(Object)} + * or an empty optional into an {@link #empty()} {@code Observable} instance. + *

+ * + *

+ * Note that the operator takes an already instantiated optional reference and does not + * by any means create this original optional. If the optional is to be created per + * consumer upon subscription, use {@link #defer(Supplier)} around {@code fromOptional}: + *


+     * Observable.defer(() -> Observable.fromOptional(createOptional()));
+     * 
+ *
+ *
Scheduler:
+ *
{@code fromOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the optional value + * @param optional the optional value to convert into an {@code Observable} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code optional} is {@code null} + * @since 3.0.0 + * @see #just(Object) + * @see #empty() + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable<@NonNull T> fromOptional(@NonNull Optional optional) { + Objects.requireNonNull(optional, "optional is null"); + return optional.map(Observable::just).orElseGet(Observable::empty); + } + + /** + * Signals the completion value or error of the given (hot) {@link CompletionStage}-based asynchronous calculation. + *

+ * + *

+ * Note that the operator takes an already instantiated, running or terminated {@code CompletionStage}. + * If the {@code CompletionStage} is to be created per consumer upon subscription, use {@link #defer(Supplier)} + * around {@code fromCompletionStage}: + *


+     * Observable.defer(() -> Observable.fromCompletionStage(createCompletionStage()));
+     * 
+ *

+ * If the {@code CompletionStage} completes with {@code null}, a {@link NullPointerException} is signaled. + *

+ * Canceling the flow can't cancel the execution of the {@code CompletionStage} because {@code CompletionStage} + * itself doesn't support cancellation. Instead, the operator detaches from the {@code CompletionStage}. + *

+ *
Scheduler:
+ *
{@code fromCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code CompletionStage} + * @param stage the {@code CompletionStage} to convert to {@code Observable} and signal its terminal value or error + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code stage} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable<@NonNull T> fromCompletionStage(@NonNull CompletionStage stage) { + Objects.requireNonNull(stage, "stage is null"); + return RxJavaPlugins.onAssembly(new ObservableFromCompletionStage<>(stage)); + } + + /** + * Converts a {@link Stream} into a finite {@code Observable} and emits its items in the sequence. + *

+ * + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #fromIterable(Iterable)}: + *


+     * Stream<T> stream = ...
+     * Observable.fromIterable(stream::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * IntStream intStream = IntStream.rangeClosed(1, 10);
+     * Observable.fromStream(intStream.boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Scheduler:
+ *
{@code fromStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the source {@code Stream} + * @param stream the {@code Stream} of values to emit + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code stream} is {@code null} + * @since 3.0.0 + * @see #fromIterable(Iterable) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Observable<@NonNull T> fromStream(@NonNull Stream stream) { + Objects.requireNonNull(stream, "stream is null"); + return RxJavaPlugins.onAssembly(new ObservableFromStream<>(stream)); + } + + /** + * Maps each upstream value into an {@link Optional} and emits the contained item if not empty. + *

+ * + * + *

+ *
Scheduler:
+ *
{@code mapOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the non-{@code null} output type + * @param mapper the function that receives the upstream item and should return a non-empty {@code Optional} + * to emit as the output or an empty {@code Optional} to skip to the next upstream value + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #map(Function) + * @see #filter(Predicate) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Observable mapOptional(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableMapOptional<>(this, mapper)); + } + + /** + * Collects the finite upstream's values into a container via a {@link Stream} {@link Collector} callback set and emits + * it as the success result as a {@link Single}. + *

+ * + * + *

+ *
Scheduler:
+ *
{@code collect} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the non-{@code null} result type + * @param the intermediate container type used for the accumulation + * @param collector the interface defining the container supplier, accumulator and finisher functions; + * see {@link Collectors} for some standard implementations + * @return the new {@code Single} instance + * @throws NullPointerException if {@code collector} is {@code null} + * @since 3.0.0 + * @see Collectors + * @see #collect(Supplier, BiConsumer) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R, @Nullable A> Single collect(@NonNull Collector collector) { + Objects.requireNonNull(collector, "collector is null"); + return RxJavaPlugins.onAssembly(new ObservableCollectWithCollectorSingle<>(this, collector)); + } + + /** + * Signals the first upstream item (or the default item if the upstream is empty) via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).firstStage(Optional.empty());
+     * 
+ *
+ *
Scheduler:
+ *
{@code firstStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} + * @since 3.0.0 + * @see #firstOrErrorStage() + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage firstStage(@Nullable T defaultItem) { + return subscribeWith(new ObservableFirstStageObserver<>(true, defaultItem)); + } + + /** + * Signals the only expected upstream item (or the default item if the upstream is empty) + * or signals {@link IllegalArgumentException} if the upstream has more than one item + * via a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).singleStage(Optional.empty());
+     * 
+ *
+ *
Scheduler:
+ *
{@code singleStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} + * @since 3.0.0 + * @see #singleOrErrorStage() + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage singleStage(@Nullable T defaultItem) { + return subscribeWith(new ObservableSingleStageObserver<>(true, defaultItem)); + } + + /** + * Signals the last upstream item (or the default item if the upstream is empty) via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ * {@code CompletionStage}s don't have a notion of emptiness and allow {@code null}s, therefore, one can either use + * a {@code defaultItem} of {@code null} or turn the flow into a sequence of {@link Optional}s and default to {@link Optional#empty()}: + *


+     * CompletionStage<Optional<T>> stage = source.map(Optional::of).lastStage(Optional.empty());
+     * 
+ *
+ *
Scheduler:
+ *
{@code lastStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param defaultItem the item to signal if the upstream is empty + * @return the new {@code CompletionStage} instance + * @throws NullPointerException if {@code defaultItem} is {@code null} + * @since 3.0.0 + * @see #lastOrErrorStage() + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage lastStage(@Nullable T defaultItem) { + return subscribeWith(new ObservableLastStageObserver<>(true, defaultItem)); + } + + /** + * Signals the first upstream item or a {@link NoSuchElementException} if the upstream is empty via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Scheduler:
+ *
{@code firstOrErrorStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #firstStage(Object) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage firstOrErrorStage() { + return subscribeWith(new ObservableFirstStageObserver<>(false, null)); + } + + /** + * Signals the only expected upstream item, a {@link NoSuchElementException} if the upstream is empty + * or signals {@link IllegalArgumentException} if the upstream has more than one item + * via a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Scheduler:
+ *
{@code singleOrErrorStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #singleStage(Object) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage singleOrErrorStage() { + return subscribeWith(new ObservableSingleStageObserver<>(false, null)); + } + + /** + * Signals the last upstream item or a {@link NoSuchElementException} if the upstream is empty via + * a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Scheduler:
+ *
{@code lastOrErrorStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + * @see #lastStage(Object) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage lastOrErrorStage() { + return subscribeWith(new ObservableLastStageObserver<>(false, null)); + } + + /** + * Creates a sequential {@link Stream} to consume or process the current {@code Observable} in a blocking manner via + * the Java {@code Stream} API. + *

+ * + *

+ * Cancellation of the upstream is done via {@link Stream#close()}, therefore, it is strongly recommended the + * consumption is performed within a try-with-resources construct: + *


+     * Observable<Integer> source = Observable.range(1, 10)
+     *        .subscribeOn(Schedulers.computation());
+     *
+     * try (Stream<Integer> stream = source.blockingStream()) {
+     *     stream.limit(3).forEach(System.out::println);
+     * }
+     * 
+ *
+ *
Scheduler:
+ *
{@code blockingStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @return the new {@code Stream} instance + * @since 3.0.0 + * @see #blockingStream(int) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Stream blockingStream() { + return blockingStream(bufferSize()); + } + + /** + * Creates a sequential {@link Stream} to consume or process the current {@code Observable} in a blocking manner via + * the Java {@code Stream} API. + *

+ * + *

+ * Cancellation of the upstream is done via {@link Stream#close()}, therefore, it is strongly recommended the + * consumption is performed within a try-with-resources construct: + *


+     * Observable<Integer> source = Observable.range(1, 10)
+     *        .subscribeOn(Schedulers.computation());
+     *
+     * try (Stream<Integer> stream = source.blockingStream(4)) {
+     *     stream.limit(3).forEach(System.out::println);
+     * }
+     * 
+ *
+ *
Scheduler:
+ *
{@code blockingStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param capacityHint the expected number of items to be buffered + * @return the new {@code Stream} instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Stream blockingStream(int capacityHint) { + Iterator iterator = blockingIterable(capacityHint).iterator(); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false) + .onClose(((Disposable) iterator)::dispose); + } + + /** + * Maps each upstream item into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #concatMapIterable(Function)}: + *


+     * source.concatMapIterable(v -> createStream(v)::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.concatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Scheduler:
+ *
{@code concatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @return the new {@code Observable} instance + * @since 3.0.0 + * @throws NullPointerException if {@code mapper} is {@code null} + * @see #concatMap(Function) + * @see #concatMapIterable(Function) + * @see #flatMapStream(Function) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Observable concatMapStream(@NonNull Function> mapper) { + return flatMapStream(mapper); + } + + /** + * Maps each upstream item into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flatMapIterable(Function)}: + *


+     * source.flatMapIterable(v -> createStream(v)::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Scheduler:
+ *
{@code flatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @return the new {@code Observable} instance + * @since 3.0.0 + * @throws NullPointerException if {@code mapper} is {@code null} + * @see #flatMap(Function) + * @see #flatMapIterable(Function) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Observable flatMapStream(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ObservableFlatMapStream<>(this, mapper)); + } } diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableConverter.java b/src/main/java/io/reactivex/rxjava3/core/ObservableConverter.java index 744c0ef48e..24cb2c9fad 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableConverter.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableConverter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,20 +16,20 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Convenience interface and callback used by the {@link Observable#to} operator to turn an Observable into another + * Convenience interface and callback used by the {@link Observable#to} operator to turn an {@link Observable} into another * value fluently. *

History: 2.1.7 - experimental * @param the upstream type * @param the output type * @since 2.2 */ -public interface ObservableConverter { +@FunctionalInterface +public interface ObservableConverter<@NonNull T, @NonNull R> { /** - * Applies a function to the upstream Observable and returns a converted value of type {@code R}. + * Applies a function to the upstream {@link Observable} and returns a converted value of type {@code R}. * - * @param upstream the upstream Observable instance + * @param upstream the upstream {@code Observable} instance * @return the converted value */ - @NonNull R apply(@NonNull Observable upstream); } diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableEmitter.java b/src/main/java/io/reactivex/rxjava3/core/ObservableEmitter.java index f4f7ef6bb0..0658c4ba81 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableEmitter.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableEmitter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.functions.Cancellable; +import io.reactivex.rxjava3.functions.*; /** * Abstraction over an RxJava {@link Observer} that allows associating @@ -47,19 +47,21 @@ * * @param the value type to emit */ -public interface ObservableEmitter extends Emitter { +public interface ObservableEmitter<@NonNull T> extends Emitter { /** - * Sets a Disposable on this emitter; any previous {@link Disposable} + * Sets a {@link Disposable} on this emitter; any previous {@code Disposable} * or {@link Cancellable} will be disposed/cancelled. - * @param d the disposable, null is allowed + *

This method is thread-safe. + * @param d the {@code Disposable}, {@code null} is allowed */ void setDisposable(@Nullable Disposable d); /** - * Sets a Cancellable on this emitter; any previous {@link Disposable} - * or {@link Cancellable} will be disposed/cancelled. - * @param c the cancellable resource, null is allowed + * Sets a {@link Cancellable} on this emitter; any previous {@link Disposable} + * or {@code Cancellable} will be disposed/cancelled. + *

This method is thread-safe. + * @param c the {@code Cancellable} resource, {@code null} is allowed */ void setCancellable(@Nullable Cancellable c); @@ -73,21 +75,21 @@ public interface ObservableEmitter extends Emitter { boolean isDisposed(); /** - * Ensures that calls to onNext, onError and onComplete are properly serialized. - * @return the serialized ObservableEmitter + * Ensures that calls to {@code onNext}, {@code onError} and {@code onComplete} are properly serialized. + * @return the serialized {@link ObservableEmitter} */ @NonNull ObservableEmitter serialize(); /** - * Attempts to emit the specified {@code Throwable} error if the downstream + * Attempts to emit the specified {@link Throwable} error if the downstream * hasn't cancelled the sequence or is otherwise terminated, returning false * if the emission is not allowed to happen due to lifecycle restrictions. *

- * Unlike {@link #onError(Throwable)}, the {@code RxJavaPlugins.onError} is not called - * if the error could not be delivered. + * Unlike {@link #onError(Throwable)}, the {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable) RxjavaPlugins.onError} + * is not called if the error could not be delivered. *

History: 2.1.1 - experimental - * @param t the throwable error to signal if possible + * @param t the {@code Throwable} error to signal if possible * @return true if successful, false if the downstream is not able to accept further * events * @since 2.2 diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java index 1f98911285..056441620a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableOnSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,22 +10,24 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of an {@link ObservableEmitter} instance that allows pushing + * an {@link ObservableEmitter} instance that allows pushing * events in a cancellation-safe manner. * * @param the value type pushed */ -public interface ObservableOnSubscribe { +@FunctionalInterface +public interface ObservableOnSubscribe<@NonNull T> { /** - * Called for each Observer that subscribes. - * @param emitter the safe emitter instance, never null + * Called for each {@link Observer} that subscribes. + * @param emitter the safe emitter instance, never {@code null} * @throws Throwable on error */ void subscribe(@NonNull ObservableEmitter emitter) throws Throwable; diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableOperator.java b/src/main/java/io/reactivex/rxjava3/core/ObservableOperator.java index f7335b6221..fd697fa626 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableOperator.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableOperator.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,18 +16,19 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to map/wrap a downstream observer to an upstream observer. + * Interface to map/wrap a downstream {@link Observer} to an upstream {@code Observer}. * * @param the value type of the downstream * @param the value type of the upstream */ -public interface ObservableOperator { +@FunctionalInterface +public interface ObservableOperator<@NonNull Downstream, @NonNull Upstream> { /** - * Applies a function to the child Observer and returns a new parent Observer. - * @param observer the child Observer instance - * @return the parent Observer instance - * @throws Exception on failure + * Applies a function to the child {@link Observer} and returns a new parent {@code Observer}. + * @param observer the child {@code Observer} instance + * @return the parent {@code Observer} instance + * @throws Throwable on failure */ @NonNull - Observer apply(@NonNull Observer observer) throws Exception; + Observer apply(@NonNull Observer observer) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableSource.java b/src/main/java/io/reactivex/rxjava3/core/ObservableSource.java index a48011e438..c00bfc2170 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableSource.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; @@ -21,12 +22,13 @@ * @param the element type * @since 2.0 */ -public interface ObservableSource { +@FunctionalInterface +public interface ObservableSource<@NonNull T> { /** - * Subscribes the given Observer to this ObservableSource instance. - * @param observer the Observer, not null - * @throws NullPointerException if {@code observer} is null + * Subscribes the given {@link Observer} to this {@link ObservableSource} instance. + * @param observer the {@code Observer}, not {@code null} + * @throws NullPointerException if {@code observer} is {@code null} */ void subscribe(@NonNull Observer observer); } diff --git a/src/main/java/io/reactivex/rxjava3/core/ObservableTransformer.java b/src/main/java/io/reactivex/rxjava3/core/ObservableTransformer.java index 63dc87ca8b..57b32bdc71 100644 --- a/src/main/java/io/reactivex/rxjava3/core/ObservableTransformer.java +++ b/src/main/java/io/reactivex/rxjava3/core/ObservableTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,17 +16,18 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to compose Observables. + * Interface to compose {@link Observable}s. * * @param the upstream value type * @param the downstream value type */ -public interface ObservableTransformer { +@FunctionalInterface +public interface ObservableTransformer<@NonNull Upstream, @NonNull Downstream> { /** - * Applies a function to the upstream Observable and returns an ObservableSource with + * Applies a function to the upstream {@link Observable} and returns an {@link ObservableSource} with * optionally different element type. - * @param upstream the upstream Observable instance - * @return the transformed ObservableSource instance + * @param upstream the upstream {@code Observable} instance + * @return the transformed {@code ObservableSource} instance */ @NonNull ObservableSource apply(@NonNull Observable upstream); diff --git a/src/main/java/io/reactivex/rxjava3/core/Observer.java b/src/main/java/io/reactivex/rxjava3/core/Observer.java index ef95e18034..6b911f51e5 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Observer.java +++ b/src/main/java/io/reactivex/rxjava3/core/Observer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -73,20 +73,20 @@ * @param * the type of item the Observer expects to observe */ -public interface Observer { +public interface Observer<@NonNull T> { /** - * Provides the Observer with the means of cancelling (disposing) the - * connection (channel) with the Observable in both + * Provides the {@link Observer} with the means of cancelling (disposing) the + * connection (channel) with the {@link Observable} in both * synchronous (from within {@link #onNext(Object)}) and asynchronous manner. - * @param d the Disposable instance whose {@link Disposable#dispose()} can + * @param d the {@link Disposable} instance whose {@link Disposable#dispose()} can * be called anytime to cancel the connection * @since 2.0 */ void onSubscribe(@NonNull Disposable d); /** - * Provides the Observer with a new item to observe. + * Provides the {@link Observer} with a new item to observe. *

* The {@link Observable} may call this method 0 or more times. *

@@ -99,9 +99,9 @@ public interface Observer { void onNext(@NonNull T t); /** - * Notifies the Observer that the {@link Observable} has experienced an error condition. + * Notifies the {@link Observer} that the {@link Observable} has experienced an error condition. *

- * If the {@link Observable} calls this method, it will not thereafter call {@link #onNext} or + * If the {@code Observable} calls this method, it will not thereafter call {@link #onNext} or * {@link #onComplete}. * * @param e @@ -110,9 +110,9 @@ public interface Observer { void onError(@NonNull Throwable e); /** - * Notifies the Observer that the {@link Observable} has finished sending push-based notifications. + * Notifies the {@link Observer} that the {@link Observable} has finished sending push-based notifications. *

- * The {@link Observable} will not call this method if it calls {@link #onError}. + * The {@code Observable} will not call this method if it calls {@link #onError}. */ void onComplete(); diff --git a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java index a300cb02ce..3aa001127a 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Scheduler.java +++ b/src/main/java/io/reactivex/rxjava3/core/Scheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,15 +13,14 @@ package io.reactivex.rxjava3.core; +import java.util.Objects; import java.util.concurrent.TimeUnit; import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; import io.reactivex.rxjava3.internal.schedulers.*; -import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.SchedulerRunnableIntrospection; @@ -61,8 +60,9 @@ * interface which can grant access to the original or hooked {@code Runnable}, thus, a repeated {@code RxJavaPlugins.onSchedule} * can detect the earlier hook and not apply a new one over again. *

- * The default implementation of {@link #now(TimeUnit)} and {@link Worker#now(TimeUnit)} methods to return current - * {@link System#currentTimeMillis()} value in the desired time unit. Custom {@code Scheduler} implementations can override this + * The default implementation of {@link #now(TimeUnit)} and {@link Worker#now(TimeUnit)} methods to return current {@link System#currentTimeMillis()} + * value in the desired time unit, unless {@code rx3.scheduler.use-nanotime} (boolean) is set. When the property is set to + * {@code true}, the method uses {@link System#nanoTime()} as its basis instead. Custom {@code Scheduler} implementations can override this * to provide specialized time accounting (such as virtual time to be advanced programmatically). * Note that operators requiring a {@code Scheduler} may rely on either of the {@code now()} calls provided by * {@code Scheduler} or {@code Worker} respectively, therefore, it is recommended they represent a logically @@ -74,14 +74,14 @@ * based on the relative time between it and {@link Worker#now(TimeUnit)}. However, drifts or changes in the * system clock could affect this calculation either by scheduling subsequent runs too frequently or too far apart. * Therefore, the default implementation uses the {@link #clockDriftTolerance()} value (set via - * {@code rx3.scheduler.drift-tolerance} in minutes) to detect a drift in {@link Worker#now(TimeUnit)} and - * re-adjust the absolute/relative time calculation accordingly. + * {@code rx3.scheduler.drift-tolerance} and {@code rx3.scheduler.drift-tolerance-unit}) to detect a + * drift in {@link Worker#now(TimeUnit)} and re-adjust the absolute/relative time calculation accordingly. *

* The default implementations of {@link #start()} and {@link #shutdown()} do nothing and should be overridden if the * underlying task-execution scheme supports stopping and restarting itself. *

* If the {@code Scheduler} is shut down or a {@code Worker} is disposed, the {@code schedule} methods - * should return the {@link io.reactivex.rxjava3.disposables.Disposables#disposed()} singleton instance indicating the shut down/disposed + * should return the {@link Disposable#disposed()} singleton instance indicating the shut down/disposed * state to the caller. Since the shutdown or dispose can happen from any thread, the {@code schedule} implementations * should make best effort to cancel tasks immediately after those tasks have been submitted to the * underlying task-execution scheme if the shutdown/dispose was detected after this submission. @@ -89,20 +89,73 @@ * All methods on the {@code Scheduler} and {@code Worker} classes should be thread safe. */ public abstract class Scheduler { + /** + * Value representing whether to use {@link System#nanoTime()}, or default as clock for {@link #now(TimeUnit)} + * and {@link Scheduler.Worker#now(TimeUnit)}. + *

+ * Associated system parameter: + *

    + *
  • {@code rx3.scheduler.use-nanotime}, boolean, default {@code false} + *
+ */ + static boolean IS_DRIFT_USE_NANOTIME = Boolean.getBoolean("rx3.scheduler.use-nanotime"); + + /** + * Returns the current clock time depending on state of {@link Scheduler#IS_DRIFT_USE_NANOTIME} in given {@code unit} + *

+ * By default {@link System#currentTimeMillis()} will be used as the clock. When the property is set + * {@link System#nanoTime()} will be used. + *

+ * @param unit the time unit + * @return the 'current time' in given unit + * @throws NullPointerException if {@code unit} is {@code null} + */ + static long computeNow(TimeUnit unit) { + if (!IS_DRIFT_USE_NANOTIME) { + return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + return unit.convert(System.nanoTime(), TimeUnit.NANOSECONDS); + } + /** * The tolerance for a clock drift in nanoseconds where the periodic scheduler will rebase. *

- * The associated system parameter, {@code rx3.scheduler.drift-tolerance}, expects its value in minutes. + * Associated system parameters: + *

    + *
  • {@code rx3.scheduler.drift-tolerance}, long, default {@code 15}
  • + *
  • {@code rx3.scheduler.drift-tolerance-unit}, string, default {@code minutes}, + * supports {@code seconds} and {@code milliseconds}. + *
+ */ + static final long CLOCK_DRIFT_TOLERANCE_NANOSECONDS = + computeClockDrift( + Long.getLong("rx3.scheduler.drift-tolerance", 15), + System.getProperty("rx3.scheduler.drift-tolerance-unit", "minutes") + ); + + /** + * Returns the clock drift tolerance in nanoseconds based on the input selection. + * @param time the time value + * @param timeUnit the time unit string + * @return the time amount in nanoseconds */ - static final long CLOCK_DRIFT_TOLERANCE_NANOSECONDS; - static { - CLOCK_DRIFT_TOLERANCE_NANOSECONDS = TimeUnit.MINUTES.toNanos( - Long.getLong("rx3.scheduler.drift-tolerance", 15)); + static long computeClockDrift(long time, String timeUnit) { + if ("seconds".equalsIgnoreCase(timeUnit)) { + return TimeUnit.SECONDS.toNanos(time); + } else if ("milliseconds".equalsIgnoreCase(timeUnit)) { + return TimeUnit.MILLISECONDS.toNanos(time); + } + return TimeUnit.MINUTES.toNanos(time); } /** * Returns the clock drift tolerance in nanoseconds. - *

Related system property: {@code rx3.scheduler.drift-tolerance} in minutes. + *

Related system properties: + *

    + *
  • {@code rx3.scheduler.drift-tolerance}, long, default {@code 15}
  • + *
  • {@code rx3.scheduler.drift-tolerance-unit}, string, default {@code minutes}, + * supports {@code seconds} and {@code milliseconds}. + *
* @return the tolerance in nanoseconds * @since 2.0 */ @@ -128,10 +181,11 @@ public static long clockDriftTolerance() { * Returns the 'current time' of the Scheduler in the specified time unit. * @param unit the time unit * @return the 'current time' + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.0 */ public long now(@NonNull TimeUnit unit) { - return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); + return computeNow(unit); } /** @@ -172,6 +226,7 @@ public void shutdown() { * @param run the task to execute * * @return the Disposable instance that let's one cancel this particular task. + * @throws NullPointerException if {@code run} is {@code null} * @since 2.0 */ @NonNull @@ -190,6 +245,7 @@ public Disposable scheduleDirect(@NonNull Runnable run) { * @param delay the delay amount, non-positive values indicate non-delayed scheduling * @param unit the unit of measure of the delay amount * @return the Disposable that let's one cancel this particular delayed task. + * @throws NullPointerException if {@code run} or {@code unit} is {@code null} * @since 2.0 */ @NonNull @@ -222,6 +278,7 @@ public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull Tim * @param period the period at which the task should be re-executed * @param unit the unit of measure of the delay amount * @return the Disposable that let's one cancel this particular delayed task. + * @throws NullPointerException if {@code run} or {@code unit} is {@code null} * @since 2.0 */ @NonNull @@ -293,7 +350,7 @@ public Disposable schedulePeriodicallyDirect(@NonNull Runnable run, long initial * }); *
* - * Slowing down the rate to no more than than 1 a second. This suffers from + * Slowing down the rate to no more than 1 a second. This suffers from * the same problem as the one above I could find an {@link Flowable} * operator that limits the rate without dropping the values (aka leaky * bucket algorithm). @@ -313,11 +370,13 @@ public Disposable schedulePeriodicallyDirect(@NonNull Runnable run, long initial * @param combine the function that takes a two-level nested Flowable sequence of a Completable and returns * the Completable that will be subscribed to and should trigger the execution of the scheduled Actions. * @return the Scheduler with the customized execution behavior + * @throws NullPointerException if {@code combine} is {@code null} * @since 2.1 */ @SuppressWarnings("unchecked") @NonNull public S when(@NonNull Function>, Completable> combine) { + Objects.requireNonNull(combine, "combine is null"); return (S) new SchedulerWhen(combine, this); } @@ -332,8 +391,9 @@ public S when(@NonNull Function - * The default implementation of the {@link #now(TimeUnit)} method returns current - * {@link System#currentTimeMillis()} value in the desired time unit. Custom {@code Worker} implementations can override this + * The default implementation of the {@link #now(TimeUnit)} method returns current {@link System#currentTimeMillis()} + * value in the desired time unit, unless {@code rx3.scheduler.use-nanotime} (boolean) is set. When the property is set to + * {@code true}, the method uses {@link System#nanoTime()} as its basis instead. Custom {@code Worker} implementations can override this * to provide specialized time accounting (such as virtual time to be advanced programmatically). * Note that operators requiring a scheduler may rely on either of the {@code now()} calls provided by * {@code Scheduler} or {@code Worker} respectively, therefore, it is recommended they represent a logically @@ -345,11 +405,11 @@ public S when(@NonNull Function * If the {@code Worker} is disposed, the {@code schedule} methods - * should return the {@link io.reactivex.rxjava3.disposables.Disposables#disposed()} singleton instance indicating the disposed + * should return the {@link Disposable#disposed()} singleton instance indicating the disposed * state to the caller. Since the {@link #dispose()} call can happen on any thread, the {@code schedule} implementations * should make best effort to cancel tasks immediately after those tasks have been submitted to the * underlying task-execution scheme if the dispose was detected after this submission. @@ -365,6 +425,7 @@ public abstract static class Worker implements Disposable { * @param run * Runnable to schedule * @return a Disposable to be able to unsubscribe the action (cancel it if not executed) + * @throws NullPointerException if {@code run} is {@code null} */ @NonNull public Disposable schedule(@NonNull Runnable run) { @@ -386,6 +447,7 @@ public Disposable schedule(@NonNull Runnable run) { * @param unit * the time unit of {@code delayTime} * @return a Disposable to be able to unsubscribe the action (cancel it if not executed) + * @throws NullPointerException if {@code run} or {@code unit} is {@code null} */ @NonNull public abstract Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit); @@ -417,6 +479,7 @@ public Disposable schedule(@NonNull Runnable run) { * @param unit * the time unit of {@code period} * @return a Disposable to be able to unsubscribe the action (cancel it if not executed) + * @throws NullPointerException if {@code run} or {@code unit} is {@code null} */ @NonNull public Disposable schedulePeriodically(@NonNull Runnable run, final long initialDelay, final long period, @NonNull final TimeUnit unit) { @@ -445,10 +508,11 @@ public Disposable schedulePeriodically(@NonNull Runnable run, final long initial * Returns the 'current time' of the Worker in the specified time unit. * @param unit the time unit * @return the 'current time' + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.0 */ public long now(@NonNull TimeUnit unit) { - return unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); + return computeNow(unit); } /** @@ -531,9 +595,10 @@ public void run() { try { run.run(); } catch (Throwable ex) { - Exceptions.throwIfFatal(ex); - worker.dispose(); - throw ExceptionHelper.wrapOrThrow(ex); + // Exceptions.throwIfFatal(ex); nowhere to go + dispose(); + RxJavaPlugins.onError(ex); + throw ex; } } } @@ -575,7 +640,13 @@ static final class DisposeTask implements Disposable, Runnable, SchedulerRunnabl public void run() { runner = Thread.currentThread(); try { - decoratedRun.run(); + try { + decoratedRun.run(); + } catch (Throwable ex) { + // Exceptions.throwIfFatal(e); nowhere to go + RxJavaPlugins.onError(ex); + throw ex; + } } finally { dispose(); runner = null; diff --git a/src/main/java/io/reactivex/rxjava3/core/Single.java b/src/main/java/io/reactivex/rxjava3/core/Single.java index 3cffa8dfad..6cf5a3f789 100644 --- a/src/main/java/io/reactivex/rxjava3/core/Single.java +++ b/src/main/java/io/reactivex/rxjava3/core/Single.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,34 +13,36 @@ package io.reactivex.rxjava3.core; -import java.util.NoSuchElementException; +import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; -import org.reactivestreams.Publisher; +import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.jdk8.*; import io.reactivex.rxjava3.internal.observers.*; import io.reactivex.rxjava3.internal.operators.completable.*; import io.reactivex.rxjava3.internal.operators.flowable.*; import io.reactivex.rxjava3.internal.operators.maybe.*; -import io.reactivex.rxjava3.internal.operators.mixed.SingleFlatMapObservable; -import io.reactivex.rxjava3.internal.operators.observable.*; +import io.reactivex.rxjava3.internal.operators.mixed.*; +import io.reactivex.rxjava3.internal.operators.observable.ObservableSingleSingle; import io.reactivex.rxjava3.internal.operators.single.*; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.schedulers.*; /** * The {@code Single} class implements the Reactive Pattern for a single value response. *

* {@code Single} behaves similarly to {@link Observable} except that it can only emit either a single successful - * value or an error (there is no "onComplete" notification as there is for an {@link Observable}). + * value or an error (there is no {@code onComplete} notification as there is for an {@code Observable}). *

* The {@code Single} class implements the {@link SingleSource} base interface and the default consumer * type it interacts with is the {@link SingleObserver} via the {@link #subscribe(SingleObserver)} method. @@ -63,9 +65,9 @@ *

* The documentation for this class makes use of marble diagrams. The following legend explains these diagrams: *

- * + * *

- * See {@link Flowable} or {@link Observable} for the + * See {@link Flowable} or {@code Observable} for the * implementation of the Reactive Pattern for a stream or vector of values. *

* For more information see the ReactiveX @@ -107,40 +109,41 @@ * allow working with a {@code SingleObserver} (or subclass) instance to be applied with in * a fluent manner (such as in the example above). * @param - * the type of the item emitted by the Single + * the type of the item emitted by the {@code Single} * @since 2.0 * @see io.reactivex.rxjava3.observers.DisposableSingleObserver */ -public abstract class Single implements SingleSource { +public abstract class Single<@NonNull T> implements SingleSource { /** - * Runs multiple SingleSources and signals the events of the first one that signals (disposing + * Runs multiple {@link SingleSource}s and signals the events of the first one that signals (disposing * the rest). *

- * + * *

*
Scheduler:
*
{@code amb} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Iterable sequence of sources. A subscription to each source will - * occur in the same order as in this Iterable. - * @return the new Single instance + * @param sources the {@link Iterable} sequence of sources. A subscription to each source will + * occur in the same order as in this {@code Iterable}. + * @return the new {@code Single} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single amb(final Iterable> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new SingleAmb(null, sources)); + public static <@NonNull T> Single amb(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new SingleAmb<>(null, sources)); } /** - * Runs multiple SingleSources and signals the events of the first one that signals (disposing + * Runs multiple {@link SingleSource}s and signals the events of the first one that signals (disposing * the rest). *

- * + * *

*
Scheduler:
*
{@code ambArray} does not operate by default on a particular {@link Scheduler}.
@@ -148,127 +151,135 @@ public static Single amb(final Iterable the value type * @param sources the array of sources. A subscription to each source will * occur in the same order as in this array. - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single ambArray(final SingleSource... sources) { + @SafeVarargs + @NonNull + public static <@NonNull T> Single ambArray(@NonNull SingleSource... sources) { + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { - return error(SingleInternalHelper.emptyThrower()); + return error(SingleInternalHelper.emptyThrower()); } if (sources.length == 1) { - return wrap((SingleSource)sources[0]); + @SuppressWarnings("unchecked") + SingleSource source = (SingleSource)sources[0]; + return wrap(source); } - return RxJavaPlugins.onAssembly(new SingleAmb(sources, null)); + return RxJavaPlugins.onAssembly(new SingleAmb<>(sources, null)); } /** - * Concatenate the single values, in a non-overlapping fashion, of the SingleSources provided by - * an Iterable sequence. + * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by + * an {@link Iterable} sequence. *

- * + * *

*
Backpressure:
- *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
The returned {@link Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Iterable sequence of SingleSource instances - * @return the new Flowable instance + * @param sources the {@code Iterable} sequence of {@code SingleSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.FULL) - public static Flowable concat(Iterable> sources) { - return concat(Flowable.fromIterable(sources)); + public static <@NonNull T> Flowable concat(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + return Flowable.fromIterable(sources).concatMapSingleDelayError(Functions.identity(), false); } /** - * Concatenate the single values, in a non-overlapping fashion, of the SingleSources provided by - * an Observable sequence. + * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by + * an {@link ObservableSource} sequence. *

- * + * *

*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the ObservableSource of SingleSource instances - * @return the new Observable instance + * @param sources the {@code ObservableSource} of {@code SingleSource} instances + * @return the new {@link Observable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Observable concat(ObservableSource> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new ObservableConcatMap(sources, SingleInternalHelper.toObservable(), 2, ErrorMode.IMMEDIATE)); + public static <@NonNull T> Observable concat(@NonNull ObservableSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new ObservableConcatMapSingle<>(sources, Functions.identity(), ErrorMode.IMMEDIATE, 2)); } /** - * Concatenate the single values, in a non-overlapping fashion, of the SingleSources provided by - * a Publisher sequence. + * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by + * a {@link Publisher} sequence. *

- * + * *

*
Backpressure:
- *
The returned {@code Flowable} honors the backpressure of the downstream consumer + *
The returned {@link Flowable} honors the backpressure of the downstream consumer * and the sources {@code Publisher} is expected to honor it as well.
*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Publisher of SingleSource instances - * @return the new Flowable instance + * @param sources the {@code Publisher} of {@code SingleSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concat(Publisher> sources) { + public static <@NonNull T> Flowable concat(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { return concat(sources, 2); } /** - * Concatenate the single values, in a non-overlapping fashion, of the SingleSources provided by - * a Publisher sequence and prefetched by the specified amount. + * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided by + * a {@link Publisher} sequence and prefetched by the specified amount. *

- * + * *

*
Backpressure:
- *
The returned {@code Flowable} honors the backpressure of the downstream consumer + *
The returned {@link Flowable} honors the backpressure of the downstream consumer * and the sources {@code Publisher} is expected to honor it as well.
*
Scheduler:
*
{@code concat} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the Publisher of SingleSource instances - * @param prefetch the number of SingleSources to prefetch from the Publisher - * @return the new Flowable instance + * @param sources the {@code Publisher} of {@code SingleSource} instances + * @param prefetch the number of {@code SingleSource}s to prefetch from the {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Flowable concat(Publisher> sources, int prefetch) { - ObjectHelper.requireNonNull(sources, "sources is null"); + public static <@NonNull T> Flowable concat(@NonNull Publisher<@NonNull ? extends SingleSource> sources, int prefetch) { + Objects.requireNonNull(sources, "sources is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new FlowableConcatMapPublisher(sources, SingleInternalHelper.toFlowable(), prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new FlowableConcatMapSinglePublisher<>(sources, Functions.identity(), ErrorMode.IMMEDIATE, prefetch)); } /** - * Returns a Flowable that emits the items emitted by two Singles, one after the other. + * Returns a {@link Flowable} that emits the items emitted by two {@link SingleSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -278,29 +289,29 @@ public static Flowable concat(Publisher the common value type * @param source1 - * a Single to be concatenated + * a {@code SingleSource} to be concatenated * @param source2 - * a Single to be concatenated - * @return a Flowable that emits items emitted by the two source Singles, one after the other. + * a {@code SingleSource} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see
ReactiveX operators documentation: Concat */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concat( - SingleSource source1, SingleSource source2 + public static <@NonNull T> Flowable concat( + @NonNull SingleSource source1, @NonNull SingleSource source2 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - return concat(Flowable.fromArray(source1, source2)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + return Flowable.fromArray(source1, source2).concatMapSingleDelayError(Functions.identity(), false); } /** - * Returns a Flowable that emits the items emitted by three Singles, one after the other. + * Returns a {@link Flowable} that emits the items emitted by three {@link SingleSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -310,33 +321,33 @@ public static Flowable concat( * * @param the common value type * @param source1 - * a Single to be concatenated + * a {@code SingleSource} to be concatenated * @param source2 - * a Single to be concatenated + * a {@code SingleSource} to be concatenated * @param source3 - * a Single to be concatenated - * @return a Flowable that emits items emitted by the three source Singles, one after the other. + * a {@code SingleSource} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Concat */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concat( - SingleSource source1, SingleSource source2, - SingleSource source3 + public static <@NonNull T> Flowable concat( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - return concat(Flowable.fromArray(source1, source2, source3)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + return Flowable.fromArray(source1, source2, source3).concatMapSingleDelayError(Functions.identity(), false); } /** - * Returns a Flowable that emits the items emitted by four Singles, one after the other. + * Returns a {@link Flowable} that emits the items emitted by four {@link SingleSource}s, one after the other. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -346,119 +357,387 @@ public static Flowable concat( * * @param the common value type * @param source1 - * a Single to be concatenated + * a {@code SingleSource} to be concatenated * @param source2 - * a Single to be concatenated + * a {@code SingleSource} to be concatenated * @param source3 - * a Single to be concatenated + * a {@code SingleSource} to be concatenated * @param source4 - * a Single to be concatenated - * @return a Flowable that emits items emitted by the four source Singles, one after the other. + * a {@code SingleSource} to be concatenated + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Concat */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable concat( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4 + public static <@NonNull T> Flowable concat( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - return concat(Flowable.fromArray(source1, source2, source3, source4)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + return Flowable.fromArray(source1, source2, source3, source4).concatMapSingleDelayError(Functions.identity(), false); } /** - * Concatenate the single values, in a non-overlapping fashion, of the SingleSources provided in + * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided in * an array. *

- * + * *

*
Backpressure:
- *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
The returned {@link Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code concatArray} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources the array of SingleSource instances - * @return the new Flowable instance + * @param sources the array of {@code SingleSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Flowable concatArray(SingleSource... sources) { - return RxJavaPlugins.onAssembly(new FlowableConcatMap(Flowable.fromArray(sources), SingleInternalHelper.toFlowable(), 2, ErrorMode.BOUNDARY)); + @SafeVarargs + public static <@NonNull T> Flowable concatArray(@NonNull SingleSource... sources) { + return Flowable.fromArray(sources).concatMapSingleDelayError(Functions.identity(), false); + } + + /** + * Concatenate the single values, in a non-overlapping fashion, of the {@link SingleSource}s provided in + * an array. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@link Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code concatArrayDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources the array of {@code SingleSource} instances + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @SafeVarargs + public static <@NonNull T> Flowable concatArrayDelayError(@NonNull SingleSource... sources) { + return Flowable.fromArray(sources).concatMapSingleDelayError(Functions.identity(), true); } /** - * Concatenates a sequence of SingleSource eagerly into a single stream of values. + * Concatenates a sequence of {@link SingleSource} eagerly into a single stream of values. *

* *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source SingleSources. The operator buffers the value emitted by these SingleSources and then drains them - * in order, each one after the previous one completes. + * source {@code SingleSource}s. The operator buffers the value emitted by these {@code SingleSource}s and then drains them + * in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code SingleSource}s that need to be eagerly concatenated + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @SafeVarargs + public static <@NonNull T> Flowable concatArrayEager(@NonNull SingleSource... sources) { + return Flowable.fromArray(sources).concatMapEager(SingleInternalHelper.toFlowable()); + } + + /** + * Concatenates a sequence of {@link SingleSource} eagerly into a single stream of values. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * source {@code SingleSource}s. The operator buffers the value emitted by these {@code SingleSource}s and then drains them + * in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code SingleSource}s that need to be eagerly concatenated + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @SafeVarargs + public static <@NonNull T> Flowable concatArrayEagerDelayError(@NonNull SingleSource... sources) { + return Flowable.fromArray(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), true); + } + + /** + * Concatenates the {@link Iterable} sequence of {@link SingleSource}s into a single sequence by subscribing to each {@code SingleSource}, + * one after the other, one at a time and delays any errors till the all inner {@code SingleSource}s terminate + * as a {@link Flowable} sequence. + *

+ * *

*
Backpressure:
*
The operator honors backpressure from downstream.
*
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the common element base type + * @param sources the {@code Iterable} sequence of {@code SingleSource}s + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable concatDelayError(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + return Flowable.fromIterable(sources).concatMapSingleDelayError(Functions.identity()); + } + + /** + * Concatenates the {@link Publisher} sequence of {@link SingleSource}s into a single sequence by subscribing to each inner {@code SingleSource}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code Publisher} terminate + * as a {@link Flowable} sequence. + *

+ * + *

+ *
Backpressure:
+ *
{@code concatDelayError} fully supports backpressure.
+ *
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the common element base type + * @param sources the {@code Publisher} sequence of {@code SingleSource}s + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatDelayError(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + return Flowable.fromPublisher(sources).concatMapSingleDelayError(Functions.identity()); + } + + /** + * Concatenates the {@link Publisher} sequence of {@link SingleSource}s into a single sequence by subscribing to each inner {@code SingleSource}, + * one after the other, one at a time and delays any errors till the all inner and the outer {@code Publisher} terminate + * as a {@link Flowable} sequence. + *

+ * + *

+ *
Backpressure:
+ *
{@code concatDelayError} fully supports backpressure.
+ *
Scheduler:
+ *
{@code concatDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the common element base type + * @param sources the {@code Publisher} sequence of {@code SingleSource}s + * @param prefetch The number of upstream items to prefetch so that fresh items are + * ready to be mapped when a previous {@code SingleSource} terminates. + * The operator replenishes after half of the prefetch amount has been consumed + * and turned into {@code SingleSource}s. + * @return the new {@code Flowable} with the concatenating behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Flowable concatDelayError(@NonNull Publisher<@NonNull ? extends SingleSource> sources, int prefetch) { + return Flowable.fromPublisher(sources).concatMapSingleDelayError(Functions.identity(), true, prefetch); + } + + /** + * Concatenates an {@link Iterable} sequence of {@link SingleSource}s eagerly into a single stream of values. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * source {@code SingleSource}s. The operator buffers the values emitted by these {@code SingleSource}s and then drains them + * in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources an {@code Iterable} sequence of {@code SingleSource} that need to be eagerly concatenated + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable concatEager(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + return Flowable.fromIterable(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), false); + } + + /** + * Concatenates an {@link Iterable} sequence of {@link SingleSource}s eagerly into a single stream of values and + * runs a limited number of the inner sources at once. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * source {@code SingleSource}s. The operator buffers the values emitted by these {@code SingleSource}s and then drains them + * in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources an {@code Iterable} sequence of {@code SingleSource} that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code SingleSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code SingleSource}s can be active at the same time + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable concatEager(@NonNull Iterable<@NonNull ? extends SingleSource> sources, int maxConcurrency) { + return Flowable.fromIterable(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), false, maxConcurrency, 1); + } + + /** + * Concatenates a {@link Publisher} sequence of {@link SingleSource}s eagerly into a single stream of values. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code SingleSource}s as they are observed. The operator buffers the values emitted by these + * {@code SingleSource}s and then drains them in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the outer {@code Publisher} is + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
+ *
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Single that need to be eagerly concatenated - * @return the new Flowable instance with the specified concatenation behavior + * @param sources a sequence of {@code SingleSource}s that need to be eagerly concatenated + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatArrayEager(SingleSource... sources) { - return Flowable.fromArray(sources).concatMapEager(SingleInternalHelper.toFlowable()); + public static <@NonNull T> Flowable concatEager(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + return Flowable.fromPublisher(sources).concatMapEager(SingleInternalHelper.toFlowable()); } /** - * Concatenates a Publisher sequence of SingleSources eagerly into a single stream of values. + * Concatenates a {@link Publisher} sequence of {@link SingleSource}s eagerly into a single stream of values and + * runs a limited number of those inner {@code SingleSource}s at once. *

- * + * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * emitted source Publishers as they are observed. The operator buffers the values emitted by these - * Publishers and then drains them in order, each one after the previous one completes. + * emitted source {@code SingleSource}s as they are observed. The operator buffers the values emitted by these + * {@code SingleSource}s and then drains them in order, each one after the previous one succeeds. *

*
Backpressure:
- *
Backpressure is honored towards the downstream and the outer Publisher is + *
Backpressure is honored towards the downstream and the outer {@code Publisher} is * expected to support backpressure. Violating this assumption, the operator will * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
*
Scheduler:
*
This method does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param sources a sequence of Publishers that need to be eagerly concatenated - * @return the new Publisher instance with the specified concatenation behavior + * @param sources a sequence of {@code SingleSource}s that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code SingleSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code SingleSource}s can be active at the same time + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable concatEager(@NonNull Publisher<@NonNull ? extends SingleSource> sources, int maxConcurrency) { + return Flowable.fromPublisher(sources).concatMapEager(SingleInternalHelper.toFlowable(), maxConcurrency, 1); + } + + /** + * Concatenates an {@link Iterable} sequence of {@link SingleSource}s eagerly into a single stream of values, + * delaying errors until all the inner sources terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * source {@code SingleSource}s. The operator buffers the values emitted by these {@code SingleSource}s and then drains them + * in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources an {@code Iterable} sequence of {@code SingleSource} that need to be eagerly concatenated + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatEager(Publisher> sources) { - return Flowable.fromPublisher(sources).concatMapEager(SingleInternalHelper.toFlowable()); + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + return Flowable.fromIterable(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), true); } /** - * Concatenates a sequence of SingleSources eagerly into a single stream of values. + * Concatenates an {@link Iterable} sequence of {@link SingleSource}s eagerly into a single stream of values, + * delaying errors until all the inner sources terminate. *

- * + * *

* Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the - * source SingleSources. The operator buffers the values emitted by these SingleSources and then drains them - * in order, each one after the previous one completes. + * source {@code SingleSource}s. The operator buffers the values emitted by these {@code SingleSource}s and then drains them + * in order, each one after the previous one succeeds. *

*
Backpressure:
*
Backpressure is honored towards the downstream.
@@ -466,21 +745,91 @@ public static Flowable concatEager(PublisherThis method does not operate by default on a particular {@link Scheduler}. *
* @param the value type - * @param sources a sequence of SingleSource that need to be eagerly concatenated - * @return the new Flowable instance with the specified concatenation behavior + * @param sources an {@code Iterable} sequence of {@code SingleSource} that need to be eagerly concatenated + * @param maxConcurrency the maximum number of concurrently running inner {@code SingleSource}s; {@link Integer#MAX_VALUE} + * is interpreted as all inner {@code SingleSource}s can be active at the same time + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Iterable<@NonNull ? extends SingleSource> sources, int maxConcurrency) { + return Flowable.fromIterable(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), true, maxConcurrency, 1); + } + + /** + * Concatenates a {@link Publisher} sequence of {@link SingleSource}s eagerly into a single stream of values, + * delaying errors until all the inner and the outer sequence terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code SingleSource}s as they are observed. The operator buffers the values emitted by these + * {@code SingleSource}s and then drains them in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the outer {@code Publisher} is + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code SingleSource}s that need to be eagerly concatenated + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + return Flowable.fromPublisher(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), true); + } + + /** + * Concatenates a {@link Publisher} sequence of {@link SingleSource}s eagerly into a single stream of values, + * running at most the specified number of those inner {@code SingleSource}s at once and + * delaying errors until all the inner and the outer sequence terminate. + *

+ * + *

+ * Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the + * emitted source {@code SingleSource}s as they are observed. The operator buffers the values emitted by these + * {@code SingleSource}s and then drains them in order, each one after the previous one succeeds. + *

+ *
Backpressure:
+ *
Backpressure is honored towards the downstream and the outer {@code Publisher} is + * expected to support backpressure. Violating this assumption, the operator will + * signal {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException}.
+ *
Scheduler:
+ *
This method does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the value type + * @param sources a sequence of {@code SingleSource}s that need to be eagerly concatenated + * @param maxConcurrency the number of inner {@code SingleSource}s to run at once + * @return the new {@link Flowable} instance with the specified concatenation behavior + * @throws NullPointerException if {@code sources} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive + * @since 3.0.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable concatEager(Iterable> sources) { - return Flowable.fromIterable(sources).concatMapEager(SingleInternalHelper.toFlowable()); + public static <@NonNull T> Flowable concatEagerDelayError(@NonNull Publisher<@NonNull ? extends SingleSource> sources, int maxConcurrency) { + return Flowable.fromPublisher(sources).concatMapEagerDelayError(SingleInternalHelper.toFlowable(), true, maxConcurrency, 1); } /** - * Provides an API (via a cold Single) that bridges the reactive world with the callback-style world. + * Provides an API (via a cold {@code Single}) that bridges the reactive world with the callback-style world. *

- * + * *

* Example: *


@@ -507,108 +856,113 @@ public static  Flowable concatEager(Iterable
      *  
Scheduler:
*
{@code create} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param source the emitter that is called when a SingleObserver subscribes to the returned {@code Single} - * @return the new Single instance + * @param source the emitter that is called when a {@code SingleObserver} subscribes to the returned {@code Single} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source} is {@code null} * @see SingleOnSubscribe * @see Cancellable */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single create(SingleOnSubscribe source) { - ObjectHelper.requireNonNull(source, "source is null"); - return RxJavaPlugins.onAssembly(new SingleCreate(source)); + public static <@NonNull T> Single create(@NonNull SingleOnSubscribe source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new SingleCreate<>(source)); } /** * Calls a {@link Supplier} for each individual {@link SingleObserver} to return the actual {@link SingleSource} to * be subscribed to. *

- * + * *

*
Scheduler:
*
{@code defer} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param singleSupplier the {@code Supplier} that is called for each individual {@code SingleObserver} and - * returns a SingleSource instance to subscribe to - * @return the new Single instance + * @param supplier the {@code Supplier} that is called for each individual {@code SingleObserver} and + * returns a {@code SingleSource} instance to subscribe to + * @throws NullPointerException if {@code supplier} is {@code null} + * @return the new {@code Single} instance */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single defer(final Supplier> singleSupplier) { - ObjectHelper.requireNonNull(singleSupplier, "singleSupplier is null"); - return RxJavaPlugins.onAssembly(new SingleDefer(singleSupplier)); + public static <@NonNull T> Single defer(@NonNull Supplier> supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new SingleDefer<>(supplier)); } /** - * Signals a Throwable returned by the callback function for each individual SingleObserver. + * Signals a {@link Throwable} returned by the callback function for each individual {@link SingleObserver}. *

- * + * *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param errorSupplier the Supplier that is called for each individual SingleObserver and - * returns a Throwable instance to be emitted. - * @return the new Single instance + * @param supplier the {@link Supplier} that is called for each individual {@code SingleObserver} and + * returns a {@code Throwable} instance to be emitted. + * @throws NullPointerException if {@code supplier} is {@code null} + * @return the new {@code Single} instance */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single error(final Supplier errorSupplier) { - ObjectHelper.requireNonNull(errorSupplier, "errorSupplier is null"); - return RxJavaPlugins.onAssembly(new SingleError(errorSupplier)); + public static <@NonNull T> Single error(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new SingleError<>(supplier)); } /** - * Returns a Single that invokes a subscriber's {@link SingleObserver#onError onError} method when the + * Returns a {@code Single} that invokes a subscriber's {@link SingleObserver#onError onError} method when the * subscriber subscribes to it. *

- * + * *

*
Scheduler:
*
{@code error} does not operate by default on a particular {@link Scheduler}.
*
* - * @param exception - * the particular Throwable to pass to {@link SingleObserver#onError onError} + * @param throwable + * the particular {@link Throwable} to pass to {@link SingleObserver#onError onError} * @param - * the type of the item (ostensibly) emitted by the Single - * @return a Single that invokes the subscriber's {@link SingleObserver#onError onError} method when + * the type of the item (ostensibly) emitted by the {@code Single} + * @return the new {@code Single} that invokes the subscriber's {@link SingleObserver#onError onError} method when * the subscriber subscribes to it + * @throws NullPointerException if {@code throwable} is {@code null} * @see ReactiveX operators documentation: Throw */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single error(final Throwable exception) { - ObjectHelper.requireNonNull(exception, "exception is null"); - return error(Functions.justSupplier(exception)); + public static <@NonNull T> Single error(@NonNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is null"); + return error(Functions.justSupplier(throwable)); } /** - * Returns a {@link Single} that invokes passed function and emits its result for each new SingleObserver that subscribes. + * Returns a {@code Single} that invokes the given {@link Callable} for each incoming {@link SingleObserver} + * and emits its value or exception to them. *

- * Allows you to defer execution of passed function until SingleObserver subscribes to the {@link Single}. + * Allows you to defer execution of passed function until {@code SingleObserver} subscribes to the {@code Single}. * It makes passed function "lazy". * Result of the function invocation will be emitted by the {@link Single}. *

- * + * *

*
Scheduler:
*
{@code fromCallable} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If the {@link Callable} throws an exception, the respective {@link Throwable} is + *
If the {@code Callable} throws an exception, the respective {@link Throwable} is * delivered to the downstream via {@link SingleObserver#onError(Throwable)}, * except when the downstream has disposed this {@code Single} source. * In this latter case, the {@code Throwable} is delivered to the global error handler via @@ -617,165 +971,164 @@ public static Single error(final Throwable exception) { *
* * @param callable - * function which execution should be deferred, it will be invoked when SingleObserver will subscribe to the {@link Single}. + * function which execution should be deferred, it will be invoked when {@code SingleObserver} will subscribe to the {@link Single}. * @param - * the type of the item emitted by the {@link Single}. - * @return a {@link Single} whose {@link SingleObserver}s' subscriptions trigger an invocation of the given function. + * the type of the item emitted by the {@code Single}. + * @return the new {@code Single} whose {@code SingleObserver}s' subscriptions trigger an invocation of the given function. + * @throws NullPointerException if {@code callable} is {@code null} * @see #defer(Supplier) * @see #fromSupplier(Supplier) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single fromCallable(final Callable callable) { - ObjectHelper.requireNonNull(callable, "callable is null"); - return RxJavaPlugins.onAssembly(new SingleFromCallable(callable)); + public static <@NonNull T> Single fromCallable(@NonNull Callable callable) { + Objects.requireNonNull(callable, "callable is null"); + return RxJavaPlugins.onAssembly(new SingleFromCallable<>(callable)); } /** - * Converts a {@link Future} into a {@code Single}. + * Converts a {@link Future} into a {@code Single} and awaits its outcome in a blocking fashion. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into a Single that emits the return - * value of the {@link Future#get} method of that object, by passing the object into the {@code from} - * method. + * The operator calls {@link Future#get()}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Important note: This Single is blocking; you cannot dispose it. + * A non-{@code null} value is then emitted via {@code onSuccess} or any exception is emitted via + * {@code onError}. If the {@code Future} completes with {@code null}, a {@link NullPointerException} + * is signaled. *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by + * the type of object that the {@code Future} returns, and also the type of item to be emitted by * the resulting {@code Single} - * @return a {@code Single} that emits the item from the source {@link Future} + * @return the new {@code Single} that emits the item from the source {@code Future} + * @throws NullPointerException if {@code future} is {@code null} * @see ReactiveX operators documentation: From + * @see #fromFuture(Future, long, TimeUnit) + * @see #fromCompletionStage(CompletionStage) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single fromFuture(Future future) { - return toSingle(Flowable.fromFuture(future)); + @NonNull + public static <@NonNull T> Single fromFuture(@NonNull Future future) { + return toSingle(Flowable.fromFuture(future)); } /** - * Converts a {@link Future} into a {@code Single}, with a timeout on the Future. + * Converts a {@link Future} into a {@code Single} and awaits its outcome, or timeout, in a blocking fashion. *

- * + * *

- * You can convert any object that supports the {@link Future} interface into a {@code Single} that emits - * the return value of the {@link Future#get} method of that object, by passing the object into the - * {@code from} method. + * The operator calls {@link Future#get(long, TimeUnit)}, which is a blocking method, on the subscription thread. + * It is recommended applying {@link #subscribeOn(Scheduler)} to move this blocking wait to a + * background thread, and if the {@link Scheduler} supports it, interrupt the wait when the flow + * is disposed. *

- * Important note: This {@code Single} is blocking; you cannot dispose it. + * A non-{@code null} value is then emitted via {@code onSuccess} or any exception is emitted via + * {@code onError}. If the {@code Future} completes with {@code null}, a {@link NullPointerException} + * is signaled. *

*
Scheduler:
- *
{@code fromFuture} does not operate by default on a particular {@link Scheduler}.
+ *
{@code fromFuture} does not operate by default on a particular {@code Scheduler}.
*
* * @param future - * the source {@link Future} + * the source {@code Future} * @param timeout * the maximum time to wait before calling {@code get} * @param unit * the {@link TimeUnit} of the {@code timeout} argument * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by + * the type of object that the {@code Future} returns, and also the type of item to be emitted by * the resulting {@code Single} - * @return a {@code Single} that emits the item from the source {@link Future} + * @return the new {@code Single} that emits the item from the source {@code Future} + * @throws NullPointerException if {@code future} or {@code unit} is {@code null} * @see ReactiveX operators documentation: From */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single fromFuture(Future future, long timeout, TimeUnit unit) { - return toSingle(Flowable.fromFuture(future, timeout, unit)); + @NonNull + public static <@NonNull T> Single fromFuture(@NonNull Future future, long timeout, @NonNull TimeUnit unit) { + return toSingle(Flowable.fromFuture(future, timeout, unit)); } /** - * Converts a {@link Future} into a {@code Single}, with a timeout on the Future. - *

- * - *

- * You can convert any object that supports the {@link Future} interface into a {@code Single} that emits - * the return value of the {@link Future#get} method of that object, by passing the object into the - * {@code from} method. + * Returns a {@code Single} instance that when subscribed to, subscribes to the {@link MaybeSource} instance and + * emits {@code onSuccess} as a single item, turns an {@code onComplete} into {@link NoSuchElementException} error signal or + * forwards the {@code onError} signal. *

- * Important note: This {@code Single} is blocking; you cannot dispose it. + * *

- *
Scheduler:
- *
You specify the {@link Scheduler} where the blocking wait will happen.
+ *
Scheduler:
+ *
{@code fromMaybe} does not operate by default on a particular {@link Scheduler}.
*
- * - * @param future - * the source {@link Future} - * @param timeout - * the maximum time to wait before calling {@code get} - * @param unit - * the {@link TimeUnit} of the {@code timeout} argument - * @param scheduler - * the Scheduler to use for the blocking wait - * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting {@code Single} - * @return a {@code Single} that emits the item from the source {@link Future} - * @see ReactiveX operators documentation: From + * @param the value type of the {@code MaybeSource} element + * @param maybe the {@code MaybeSource} instance to subscribe to, not {@code null} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code maybe} is {@code null} + * @since 3.0.0 */ @CheckReturnValue - @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Single fromFuture(Future future, long timeout, TimeUnit unit, Scheduler scheduler) { - return toSingle(Flowable.fromFuture(future, timeout, unit, scheduler)); + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Single fromMaybe(@NonNull MaybeSource maybe) { + Objects.requireNonNull(maybe, "maybe is null"); + return RxJavaPlugins.onAssembly(new MaybeToSingle<>(maybe, null)); } /** - * Converts a {@link Future}, operating on a specified {@link Scheduler}, into a {@code Single}. + * Returns a {@code Single} instance that when subscribed to, subscribes to the {@link MaybeSource} instance and + * emits {@code onSuccess} as a single item, emits the {@code defaultItem} for an {@code onComplete} signal or + * forwards the {@code onError} signal. *

- * - *

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

- *
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
Scheduler:
+ *
{@code fromMaybe} does not operate by default on a particular {@link Scheduler}.
*
- * - * @param future - * the source {@link Future} - * @param scheduler - * the {@link Scheduler} to wait for the Future on. Use a Scheduler such as - * {@link Schedulers#io()} that can block and wait on the Future - * @param - * the type of object that the {@link Future} returns, and also the type of item to be emitted by - * the resulting {@code Single} - * @return a {@code Single} that emits the item from the source {@link Future} - * @see ReactiveX operators documentation: From + * @param the value type of the {@code MaybeSource} element + * @param maybe the {@code MaybeSource} instance to subscribe to, not {@code null} + * @param defaultItem the item to signal if the current {@code MaybeSource} is empty + * @return the new {@code Single} instance + * @throws NullPointerException if {@code maybe} or {@code defaultItem} is {@code null} + * @since 3.0.0 */ @CheckReturnValue - @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Single fromFuture(Future future, Scheduler scheduler) { - return toSingle(Flowable.fromFuture(future, scheduler)); + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Single fromMaybe(@NonNull MaybeSource maybe, @NonNull T defaultItem) { + Objects.requireNonNull(maybe, "maybe is null"); + Objects.requireNonNull(defaultItem, "defaultItem is null"); + return RxJavaPlugins.onAssembly(new MaybeToSingle<>(maybe, defaultItem)); } /** - * Wraps a specific Publisher into a Single and signals its single element or error. - *

If the source Publisher is empty, a NoSuchElementException is signalled. If - * the source has more than one element, an IndexOutOfBoundsException is signalled. + * Wraps a specific {@link Publisher} into a {@code Single} and signals its single element or error. + *

+ * *

- * The {@link Publisher} must follow the - * Reactive-Streams specification. + * If the source {@code Publisher} is empty, a {@link NoSuchElementException} is signaled. If + * the source has more than one element, an {@link IndexOutOfBoundsException} is signaled. + *

+ * The {@code Publisher} must follow the + * Reactive Streams specification. * Violating the specification may result in undefined behavior. *

* If possible, use {@link #create(SingleOnSubscribe)} to create a * source-like {@code Single} instead. *

- * Note that even though {@link Publisher} appears to be a functional interface, it + * Note that even though {@code Publisher} appears to be a functional interface, it * is not recommended to implement it through a lambda as the specification requires * state management that is not achievable with a stateless lambda. - *

- * *

*
Backpressure:
*
The {@code publisher} is consumed in an unbounded fashion but will be cancelled @@ -784,52 +1137,55 @@ public static Single fromFuture(Future future, Scheduler sch *
{@code fromPublisher} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param publisher the source Publisher instance, not null - * @return the new Single instance + * @param publisher the source {@code Publisher} instance, not {@code null} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code publisher} is {@code null} * @see #create(SingleOnSubscribe) */ @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single fromPublisher(final Publisher publisher) { - ObjectHelper.requireNonNull(publisher, "publisher is null"); - return RxJavaPlugins.onAssembly(new SingleFromPublisher(publisher)); + public static <@NonNull T> Single fromPublisher(@NonNull Publisher publisher) { + Objects.requireNonNull(publisher, "publisher is null"); + return RxJavaPlugins.onAssembly(new SingleFromPublisher<>(publisher)); } /** - * Wraps a specific ObservableSource into a Single and signals its single element or error. - *

If the ObservableSource is empty, a NoSuchElementException is signalled. - * If the source has more than one element, an IndexOutOfBoundsException is signalled. + * Wraps a specific {@link ObservableSource} into a {@code Single} and signals its single element or error. *

- * + * + *

+ * If the {@code ObservableSource} is empty, a {@link NoSuchElementException} is signaled. + * If the source has more than one element, an {@link IndexOutOfBoundsException} is signaled. *

*
Scheduler:
*
{@code fromObservable} does not operate by default on a particular {@link Scheduler}.
*
* - * @param observableSource the source Observable, not null + * @param observable the source sequence to wrap, not {@code null} * @param - * the type of the item emitted by the {@link Single}. - * @return the new Single instance + * the type of the item emitted by the {@code Single}. + * @return the new {@code Single} instance + * @throws NullPointerException if {@code observable} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single fromObservable(ObservableSource observableSource) { - ObjectHelper.requireNonNull(observableSource, "observableSource is null"); - return RxJavaPlugins.onAssembly(new ObservableSingleSingle(observableSource, null)); + public static <@NonNull T> Single fromObservable(@NonNull ObservableSource observable) { + Objects.requireNonNull(observable, "observable is null"); + return RxJavaPlugins.onAssembly(new ObservableSingleSingle<>(observable, null)); } /** - * Returns a {@link Single} that invokes passed supplierfunction and emits its result - * for each new SingleObserver that subscribes. + * Returns a {@code Single} that invokes passed supplier and emits its result + * for each individual {@link SingleObserver} that subscribes. *

- * Allows you to defer execution of passed function until SingleObserver subscribes to the {@link Single}. + * Allows you to defer execution of passed function until a {@code SingleObserver} subscribes to the {@link Single}. * It makes passed function "lazy". * Result of the function invocation will be emitted by the {@link Single}. *

- * + * *

*
Scheduler:
*
{@code fromSupplier} does not operate by default on a particular {@link Scheduler}.
@@ -843,10 +1199,11 @@ public static Single fromObservable(ObservableSource observa *
* * @param supplier - * function which execution should be deferred, it will be invoked when SingleObserver will subscribe to the {@link Single}. + * function which execution should be deferred, it will be invoked when {@code SingleObserver} subscribes to the {@code Single}. * @param - * the type of the item emitted by the {@link Single}. - * @return a {@link Single} whose {@link SingleObserver}s' subscriptions trigger an invocation of the given function. + * the type of the item emitted by the {@code Single}. + * @return the new {@code Single} whose {@code SingleObserver}s' subscriptions trigger an invocation of the given function. + * @throws NullPointerException if {@code supplier} is {@code null} * @see #defer(Supplier) * @see #fromCallable(Callable) * @since 3.0.0 @@ -854,15 +1211,15 @@ public static Single fromObservable(ObservableSource observa @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single fromSupplier(final Supplier supplier) { - ObjectHelper.requireNonNull(supplier, "supplier is null"); - return RxJavaPlugins.onAssembly(new SingleFromSupplier(supplier)); + public static <@NonNull T> Single fromSupplier(@NonNull Supplier supplier) { + Objects.requireNonNull(supplier, "supplier is null"); + return RxJavaPlugins.onAssembly(new SingleFromSupplier<>(supplier)); } /** * Returns a {@code Single} that emits a specified item. *

- * + * *

* To convert any object into a {@code Single} that emits that object, pass that object into the * {@code just} method. @@ -875,35 +1232,36 @@ public static Single fromSupplier(final Supplier supplier) { * the item to emit * @param * the type of that item - * @return a {@code Single} that emits {@code item} + * @return the new {@code Single} that emits {@code item} + * @throws NullPointerException if {@code item} is {@code null} * @see ReactiveX operators documentation: Just */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @NonNull - public static Single just(final T item) { - ObjectHelper.requireNonNull(item, "item is null"); - return RxJavaPlugins.onAssembly(new SingleJust(item)); + public static <@NonNull T> Single just(T item) { + Objects.requireNonNull(item, "item is null"); + return RxJavaPlugins.onAssembly(new SingleJust<>(item)); } /** - * Merges an Iterable sequence of SingleSource instances into a single Flowable sequence, - * running all SingleSources at once. + * Merges an {@link Iterable} sequence of {@link SingleSource} instances into a single {@link Flowable} sequence, + * running all {@code SingleSource}s at once. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code SingleSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code SingleSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code SingleSource}s are disposed. * If more than one {@code SingleSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Iterable)} to merge sources and terminate only when all source {@code SingleSource}s @@ -911,8 +1269,9 @@ public static Single just(final T item) { *
*
* @param the common and resulting value type - * @param sources the Iterable sequence of SingleSource sources - * @return the new Flowable instance + * @param sources the {@code Iterable} sequence of {@code SingleSource} sources + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.0 * @see #mergeDelayError(Iterable) */ @@ -920,28 +1279,28 @@ public static Single just(final T item) { @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable merge(Iterable> sources) { - return merge(Flowable.fromIterable(sources)); + public static <@NonNull T> Flowable merge(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + return Flowable.fromIterable(sources).flatMapSingle(Functions.identity()); } /** - * Merges a Flowable sequence of SingleSource instances into a single Flowable sequence, - * running all SingleSources at once. + * Merges a sequence of {@link SingleSource} instances emitted by a {@link Publisher} into a single {@link Flowable} sequence, + * running all {@code SingleSource}s at once. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code SingleSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code SingleSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code SingleSource}s are disposed. * If more than one {@code SingleSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(Publisher)} to merge sources and terminate only when all source {@code SingleSource}s @@ -949,8 +1308,9 @@ public static Flowable merge(Iterable *
*
* @param the common and resulting value type - * @param sources the Flowable sequence of SingleSource sources - * @return the new Flowable instance + * @param sources the {@code Publisher} emitting a sequence of {@code SingleSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @see #mergeDelayError(Publisher) * @since 2.0 */ @@ -958,22 +1318,21 @@ public static Flowable merge(Iterable @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Flowable merge(Publisher> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapPublisher(sources, SingleInternalHelper.toFlowable(), false, Integer.MAX_VALUE, Flowable.bufferSize())); + public static <@NonNull T> Flowable merge(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableFlatMapSinglePublisher<>(sources, Functions.identity(), false, Integer.MAX_VALUE)); } /** - * Flattens a {@code Single} that emits a {@code Single} into a single {@code Single} that emits the item - * emitted by the nested {@code Single}, without any transformation. + * Flattens a {@link SingleSource} that emits a {@code SingleSingle} into a single {@code Single} that emits the item + * emitted by the nested {@code SingleSource}, without any transformation. *

- * + * *

*
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
- *
The resulting {@code Single} emits the outer source's or the inner {@code SingleSource}'s {@code Throwable} as is. - * Unlike the other {@code merge()} operators, this operator won't and can't produce a {@code CompositeException} because there is + *
The resulting {@code Single} emits the outer source's or the inner {@code SingleSource}'s {@link Throwable} as is. + * Unlike the other {@code merge()} operators, this operator won't and can't produce a {@link CompositeException} because there is * only one possibility for the outer or the inner {@code SingleSource} to emit an {@code onError} signal. * Therefore, there is no need for a {@code mergeDelayError(SingleSource>)} operator. *
@@ -982,25 +1341,25 @@ public static Flowable merge(Publisher the value type of the sources and the output * @param source * a {@code Single} that emits a {@code Single} - * @return a {@code Single} that emits the item that is the result of flattening the {@code Single} emitted + * @return the new {@code Single} that emits the item that is the result of flattening the {@code Single} emitted * by {@code source} + * @throws NullPointerException if {@code source} is {@code null} * @see ReactiveX operators documentation: Merge */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Single merge(SingleSource> source) { - ObjectHelper.requireNonNull(source, "source is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMap, T>(source, (Function)Functions.identity())); + public static <@NonNull T> Single merge(@NonNull SingleSource> source) { + Objects.requireNonNull(source, "source is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMap, T>(source, Functions.identity())); } /** - * Flattens two Singles into a single Flowable, without any transformation. + * Flattens two {@link SingleSource}s into one {@link Flowable} sequence, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as a single {@code Flowable}, by * using the {@code merge} method. *

*
Backpressure:
@@ -1008,13 +1367,13 @@ public static Single merge(SingleSourceScheduler: *
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code SingleSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code SingleSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code SingleSource}s are disposed. * If more than one {@code SingleSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(SingleSource, SingleSource)} to merge sources and terminate only when all source {@code SingleSource}s @@ -1024,10 +1383,11 @@ public static Single merge(SingleSource the common value type * @param source1 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source2 - * a SingleSource to be merged - * @return a Flowable that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} that emits all of the items emitted by the source {@code SingleSource}s + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(SingleSource, SingleSource) */ @@ -1035,21 +1395,20 @@ public static Single merge(SingleSource Flowable merge( - SingleSource source1, SingleSource source2 + public static <@NonNull T> Flowable merge( + @NonNull SingleSource source1, @NonNull SingleSource source2 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - return merge(Flowable.fromArray(source1, source2)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + return Flowable.fromArray(source1, source2).flatMapSingle(Functions.identity(), false, Integer.MAX_VALUE); } /** - * Flattens three Singles into a single Flowable, without any transformation. + * Flattens three {@link SingleSource}s into one {@link Flowable} sequence, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as a single {@code Flowable}, by * the {@code merge} method. *

*
Backpressure:
@@ -1057,13 +1416,13 @@ public static Flowable merge( *
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code SingleSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code SingleSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code SingleSource}s are disposed. * If more than one {@code SingleSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(SingleSource, SingleSource, SingleSource)} to merge sources and terminate only when all source {@code SingleSource}s @@ -1073,12 +1432,13 @@ public static Flowable merge( * * @param the common value type * @param source1 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source2 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source3 - * a SingleSource to be merged - * @return a Flowable that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} that emits all of the items emitted by the source {@code SingleSource}s + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(SingleSource, SingleSource, SingleSource) */ @@ -1086,23 +1446,22 @@ public static Flowable merge( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable merge( - SingleSource source1, SingleSource source2, - SingleSource source3 + public static <@NonNull T> Flowable merge( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - return merge(Flowable.fromArray(source1, source2, source3)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + return Flowable.fromArray(source1, source2, source3).flatMapSingle(Functions.identity(), false, Integer.MAX_VALUE); } /** - * Flattens four Singles into a single Flowable, without any transformation. + * Flattens four {@link SingleSource}s into one {@link Flowable} sequence, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as a single {@code Flowable}, by * the {@code merge} method. *

*
Backpressure:
@@ -1110,13 +1469,13 @@ public static Flowable merge( *
Scheduler:
*
{@code merge} does not operate by default on a particular {@link Scheduler}.
*
Error handling:
- *
If any of the source {@code SingleSource}s signal a {@code Throwable} via {@code onError}, the resulting + *
If any of the source {@code SingleSource}s signal a {@link Throwable} via {@code onError}, the resulting * {@code Flowable} terminates with that {@code Throwable} and all other source {@code SingleSource}s are disposed. * If more than one {@code SingleSource} signals an error, the resulting {@code Flowable} may terminate with the * first one's error or, depending on the concurrency of the sources, may terminate with a - * {@code CompositeException} containing two or more of the various error signals. + * {@link CompositeException} containing two or more of the various error signals. * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via - * {@link RxJavaPlugins#onError(Throwable)} method as {@code UndeliverableException} errors. Similarly, {@code Throwable}s + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a * (composite) error will be sent to the same global error handler. * Use {@link #mergeDelayError(SingleSource, SingleSource, SingleSource, SingleSource)} to merge sources and terminate only when all source {@code SingleSource}s @@ -1126,14 +1485,15 @@ public static Flowable merge( * * @param the common value type * @param source1 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source2 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source3 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source4 - * a SingleSource to be merged - * @return a Flowable that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} that emits all of the items emitted by the source {@code SingleSource}s + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge * @see #mergeDelayError(SingleSource, SingleSource, SingleSource, SingleSource) */ @@ -1141,47 +1501,122 @@ public static Flowable merge( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable merge( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4 + public static <@NonNull T> Flowable merge( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - return merge(Flowable.fromArray(source1, source2, source3, source4)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + return Flowable.fromArray(source1, source2, source3, source4).flatMapSingle(Functions.identity(), false, Integer.MAX_VALUE); } /** - * Merges an Iterable sequence of SingleSource instances into a single Flowable sequence, - * running all SingleSources at once and delaying any error(s) until all sources succeed or fail. + * Merges an array of {@link SingleSource} instances into a single {@link Flowable} sequence, + * running all {@code SingleSource}s at once. *

- * + * *

*
Backpressure:
- *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
The operator honors backpressure from downstream.
*
Scheduler:
- *
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
- *
- *

History: 2.1.9 - experimental - * @param the common and resulting value type - * @param sources the Iterable sequence of SingleSource sources - * @return the new Flowable instance - * @see #merge(Iterable) + *

{@code mergeArray} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If any of the source {@code SingleSource}s signal a {@link Throwable} via {@code onError}, the resulting + * {@code Flowable} terminates with that {@code Throwable} and all other source {@code SingleSource}s are disposed. + * If more than one {@code SingleSource} signals an error, the resulting {@code Flowable} may terminate with the + * first one's error or, depending on the concurrency of the sources, may terminate with a + * {@link CompositeException} containing two or more of the various error signals. + * {@code Throwable}s that didn't make into the composite will be sent (individually) to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)} method as {@link UndeliverableException} errors. Similarly, {@code Throwable}s + * signaled by source(s) after the returned {@code Flowable} has been cancelled or terminated with a + * (composite) error will be sent to the same global error handler. + * Use {@link #mergeArrayDelayError(SingleSource...)} to merge sources and terminate only when all source {@code SingleSource}s + * have completed or failed with an error. + *
+ *
+ * @param the common and resulting value type + * @param sources the array sequence of {@code SingleSource} sources + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @see #mergeArrayDelayError(SingleSource...) + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @SafeVarargs + public static <@NonNull T> Flowable mergeArray(SingleSource... sources) { + return Flowable.fromArray(sources).flatMapSingle(Functions.identity(), false, Math.max(1, sources.length)); + } + + /** + * Flattens an array of {@link SingleSource}s into one {@link Flowable}, in a way that allows a subscriber to receive all + * successfully emitted items from each of the source {@code SingleSource}s without being interrupted by an error + * notification from one of them. + *

+ * + *

+ * This behaves like {@link #merge(Publisher)} except that if any of the merged {@code SingleSource}s notify of an + * error via {@link Subscriber#onError onError}, {@code mergeArrayDelayError} will refrain from propagating that + * error notification until all of the merged {@code SingleSource}s have finished emitting items. + *

+ * Even if multiple merged {@code SingleSource}s send {@code onError} notifications, {@code mergeArrayDelayError} will only + * invoke the {@code onError} method of its subscribers once. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream.
+ *
Scheduler:
+ *
{@code mergeArrayDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the common element base type + * @param sources + * the array of {@code SingleSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @see ReactiveX operators documentation: Merge + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @SafeVarargs + @NonNull + public static <@NonNull T> Flowable mergeArrayDelayError(@NonNull SingleSource... sources) { + return Flowable.fromArray(sources).flatMapSingle(Functions.identity(), true, Math.max(1, sources.length)); + } + + /** + * Merges an {@link Iterable} sequence of {@link SingleSource} instances into one {@link Flowable} sequence, + * running all {@code SingleSource}s at once and delaying any error(s) until all sources succeed or fail. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code mergeDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 2.1.9 - experimental + * @param the common and resulting value type + * @param sources the {@code Iterable} sequence of {@code SingleSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @see #merge(Iterable) * @since 2.2 */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - public static Flowable mergeDelayError(Iterable> sources) { - return mergeDelayError(Flowable.fromIterable(sources)); + public static <@NonNull T> Flowable mergeDelayError(@NonNull Iterable<@NonNull ? extends SingleSource> sources) { + return Flowable.fromIterable(sources).flatMapSingle(Functions.identity(), true, Integer.MAX_VALUE); } /** - * Merges a Flowable sequence of SingleSource instances into a single Flowable sequence, - * running all SingleSources at once and delaying any error(s) until all sources succeed or fail. + * Merges a sequence of {@link SingleSource} instances emitted by a {@link Publisher} into a {@link Flowable} sequence, + * running all {@code SingleSource}s at once and delaying any error(s) until all sources succeed or fail. *

* *

@@ -1192,28 +1627,28 @@ public static Flowable mergeDelayError(Iterable *

History: 2.1.9 - experimental * @param the common and resulting value type - * @param sources the Flowable sequence of SingleSource sources - * @return the new Flowable instance - * @see #merge(Publisher) + * @param sources the {@code Flowable} sequence of {@code SingleSource}s + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} * @since 2.2 + * @see #merge(Publisher) */ @CheckReturnValue @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Flowable mergeDelayError(Publisher> sources) { - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new FlowableFlatMapPublisher(sources, SingleInternalHelper.toFlowable(), true, Integer.MAX_VALUE, Flowable.bufferSize())); + public static <@NonNull T> Flowable mergeDelayError(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableFlatMapSinglePublisher<>(sources, Functions.identity(), true, Integer.MAX_VALUE)); } /** - * Flattens two Singles into a single Flowable, without any transformation, delaying + * Flattens two {@link SingleSource}s into one {@link Flowable}, without any transformation, delaying * any error(s) until all sources succeed or fail. *

* *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as one {@code Flowable}, by * using the {@code mergeDelayError} method. *

*
Backpressure:
@@ -1224,10 +1659,11 @@ public static Flowable mergeDelayError(PublisherHistory: 2.1.9 - experimental * @param the common value type * @param source1 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source2 - * a SingleSource to be merged - * @return a Flowable that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} that emits all of the items emitted by the source {@code SingleSource}s + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @see ReactiveX operators documentation: Merge * @see #merge(SingleSource, SingleSource) * @since 2.2 @@ -1236,22 +1672,21 @@ public static Flowable mergeDelayError(Publisher Flowable mergeDelayError( - SingleSource source1, SingleSource source2 + public static <@NonNull T> Flowable mergeDelayError( + @NonNull SingleSource source1, @NonNull SingleSource source2 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - return mergeDelayError(Flowable.fromArray(source1, source2)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + return Flowable.fromArray(source1, source2).flatMapSingle(Functions.identity(), true, Integer.MAX_VALUE); } /** - * Flattens three Singles into a single Flowable, without any transformation, delaying + * Flattens two {@link SingleSource}s into one {@link Flowable}, without any transformation, delaying * any error(s) until all sources succeed or fail. *

* *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as one {@code Flowable}, by * the {@code mergeDelayError} method. *

*
Backpressure:
@@ -1262,12 +1697,13 @@ public static Flowable mergeDelayError( *

History: 2.1.9 - experimental * @param the common value type * @param source1 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source2 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source3 - * a SingleSource to be merged - * @return a Flowable that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} that emits all of the items emitted by the source {@code SingleSource}s + * @throws NullPointerException if {@code source1}, {@code source2} or {@code source3} is {@code null} * @see ReactiveX operators documentation: Merge * @see #merge(SingleSource, SingleSource, SingleSource) * @since 2.2 @@ -1276,24 +1712,23 @@ public static Flowable mergeDelayError( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable mergeDelayError( - SingleSource source1, SingleSource source2, - SingleSource source3 + public static <@NonNull T> Flowable mergeDelayError( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - return mergeDelayError(Flowable.fromArray(source1, source2, source3)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + return Flowable.fromArray(source1, source2, source3).flatMapSingle(Functions.identity(), true, Integer.MAX_VALUE); } /** - * Flattens four Singles into a single Flowable, without any transformation, delaying + * Flattens two {@link SingleSource}s into one {@link Flowable}, without any transformation, delaying * any error(s) until all sources succeed or fail. *

* *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as one {@code Flowable}, by * the {@code mergeDelayError} method. *

*
Backpressure:
@@ -1304,14 +1739,15 @@ public static Flowable mergeDelayError( *

History: 2.1.9 - experimental * @param the common value type * @param source1 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source2 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source3 - * a SingleSource to be merged + * a {@code SingleSource} to be merged * @param source4 - * a SingleSource to be merged - * @return a Flowable that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} that emits all of the items emitted by the source {@code SingleSource}s + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code source4} is {@code null} * @see ReactiveX operators documentation: Merge * @see #merge(SingleSource, SingleSource, SingleSource, SingleSource) * @since 2.2 @@ -1320,22 +1756,21 @@ public static Flowable mergeDelayError( @NonNull @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Flowable mergeDelayError( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4 + public static <@NonNull T> Flowable mergeDelayError( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4 ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - return mergeDelayError(Flowable.fromArray(source1, source2, source3, source4)); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + return Flowable.fromArray(source1, source2, source3, source4).flatMapSingle(Functions.identity(), true, Integer.MAX_VALUE); } /** - * Returns a singleton instance of a never-signaling Single (only calls onSubscribe). + * Returns a singleton instance of a never-signaling {@code Single} (only calls {@code onSubscribe}). *

- * + * *

*
Scheduler:
*
{@code never} does not operate by default on a particular {@link Scheduler}.
@@ -1347,81 +1782,155 @@ public static Flowable mergeDelayError( @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") - public static Single never() { + @NonNull + public static <@NonNull T> Single never() { return RxJavaPlugins.onAssembly((Single) SingleNever.INSTANCE); } /** - * Signals success with 0L value after the given delay for each SingleObserver. + * Signals success with 0L value after the given delay when a {@link SingleObserver} subscribes. *

- * + * *

*
Scheduler:
*
{@code timer} operates by default on the {@code computation} {@link Scheduler}.
*
* @param delay the delay amount * @param unit the time unit of the delay - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public static Single timer(long delay, TimeUnit unit) { + @NonNull + public static Single timer(long delay, @NonNull TimeUnit unit) { return timer(delay, unit, Schedulers.computation()); } /** - * Signals success with 0L value after the given delay for each SingleObserver. + * Signals success with 0L value on the specified {@link Scheduler} after the given + * delay when a {@link SingleObserver} subscribes. *

- * + * *

*
Scheduler:
- *
you specify the {@link Scheduler} to signal on.
+ *
you specify the {@code Scheduler} to signal on.
*
* @param delay the delay amount * @param unit the time unit of the delay - * @param scheduler the scheduler where the single 0L will be emitted - * @return the new Single instance + * @param scheduler the {@code Scheduler} where the single 0L will be emitted + * @return the new {@code Single} instance * @throws NullPointerException - * if unit is null, or - * if scheduler is null + * if {@code unit} is {@code null}, or + * if {@code scheduler} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public static Single timer(final long delay, final TimeUnit unit, final Scheduler scheduler) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); + public static Single timer(long delay, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); return RxJavaPlugins.onAssembly(new SingleTimer(delay, unit, scheduler)); } /** - * Compares two SingleSources and emits true if they emit the same value (compared via Object.equals). + * Compares two {@link SingleSource}s and emits {@code true} if they emit the same value (compared via {@link Object#equals(Object)}). *

- * + * *

*
Scheduler:
- *
{@code equals} does not operate by default on a particular {@link Scheduler}.
+ *
{@code sequenceEqual} does not operate by default on a particular {@link Scheduler}.
*
* @param the common value type - * @param first the first SingleSource instance - * @param second the second SingleSource instance - * @return the new Single instance + * @param source1 the first {@code SingleSource} instance + * @param source2 the second {@code SingleSource} instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source1} or {@code source2} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single equals(final SingleSource first, final SingleSource second) { // NOPMD - ObjectHelper.requireNonNull(first, "first is null"); - ObjectHelper.requireNonNull(second, "second is null"); - return RxJavaPlugins.onAssembly(new SingleEquals(first, second)); + public static <@NonNull T> Single sequenceEqual(@NonNull SingleSource source1, @NonNull SingleSource source2) { // NOPMD + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + return RxJavaPlugins.onAssembly(new SingleEquals<>(source1, source2)); + } + + /** + * Switches between {@link SingleSource}s emitted by the source {@link Publisher} whenever + * a new {@code SingleSource} is emitted, disposing the previously running {@code SingleSource}, + * exposing the success items as a {@link Flowable} sequence. + *

+ * + *

+ *
Backpressure:
+ *
The {@code sources} {@code Publisher} is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE}). + * The returned {@code Flowable} respects the backpressure from the downstream.
+ *
Scheduler:
+ *
{@code switchOnNext} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
The returned sequence fails with the first error signaled by the {@code sources} {@code Publisher} + * or the currently running {@code SingleSource}, disposing the rest. Late errors are + * forwarded to the global error handler via {@link RxJavaPlugins#onError(Throwable)}.
+ *
+ * @param the element type of the {@code SingleSource}s + * @param sources the {@code Publisher} sequence of inner {@code SingleSource}s to switch between + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + * @see #switchOnNextDelayError(Publisher) + * @see ReactiveX operators documentation: Switch + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable switchOnNext(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapSinglePublisher<>(sources, Functions.identity(), false)); + } + + /** + * Switches between {@link SingleSource}s emitted by the source {@link Publisher} whenever + * a new {@code SingleSource} is emitted, disposing the previously running {@code SingleSource}, + * exposing the success items as a {@link Flowable} sequence and delaying all errors from + * all of them until all terminate. + *

+ * + *

+ *
Backpressure:
+ *
The {@code sources} {@code Publisher} is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE}). + * The returned {@code Flowable} respects the backpressure from the downstream.
+ *
Scheduler:
+ *
{@code switchOnNextDelayError} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
The returned {@code Flowable} collects all errors emitted by either the {@code sources} + * {@code Publisher} or any inner {@code SingleSource} and emits them as a {@link CompositeException} + * when all sources terminate. If only one source ever failed, its error is emitted as-is at the end.
+ *
+ * @param the element type of the {@code SingleSource}s + * @param sources the {@code Publisher} sequence of inner {@code SingleSource}s to switch between + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code sources} is {@code null} + * @since 3.0.0 + * @see #switchOnNext(Publisher) + * @see ReactiveX operators documentation: Switch + */ + @BackpressureSupport(BackpressureKind.FULL) + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> Flowable switchOnNextDelayError(@NonNull Publisher<@NonNull ? extends SingleSource> sources) { + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new FlowableSwitchMapSinglePublisher<>(sources, Functions.identity(), true)); } /** - * Advanced use only: creates a Single instance without - * any safeguards by using a callback that is called with a SingleObserver. + * Advanced use only: creates a {@code Single} instance without + * any safeguards by using a callback that is called with a {@link SingleObserver}. *

* *

@@ -1429,8 +1938,9 @@ public static Single equals(final SingleSource first, *
{@code unsafeCreate} does not operate by default on a particular {@link Scheduler}.
*
* @param the value type - * @param onSubscribe the function that is called with the subscribing SingleObserver - * @return the new Single instance + * @param onSubscribe the function that is called with the subscribing {@code SingleObserver} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} * @throws IllegalArgumentException if {@code source} is a subclass of {@code Single}; such * instances don't need conversion and is possibly a port remnant from 1.x or one should use {@link #hide()} * instead. @@ -1439,45 +1949,47 @@ public static Single equals(final SingleSource first, @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single unsafeCreate(SingleSource onSubscribe) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); + public static <@NonNull T> Single unsafeCreate(@NonNull SingleSource onSubscribe) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); if (onSubscribe instanceof Single) { throw new IllegalArgumentException("unsafeCreate(Single) should be upgraded"); } - return RxJavaPlugins.onAssembly(new SingleFromUnsafeSource(onSubscribe)); + return RxJavaPlugins.onAssembly(new SingleFromUnsafeSource<>(onSubscribe)); } /** - * Allows using and disposing a resource while running a SingleSource instance generated from + * Allows using and disposing a resource while running a {@link SingleSource} instance generated from * that resource (similar to a try-with-resources). *

- * + * *

*
Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
- * @param the value type of the SingleSource generated + * @param the value type of the {@code SingleSource} generated * @param the resource type - * @param resourceSupplier the Supplier called for each SingleObserver to generate a resource Object - * @param singleFunction the function called with the returned resource - * Object from {@code resourceSupplier} and should return a SingleSource instance + * @param resourceSupplier the {@link Supplier} called for each {@link SingleObserver} to generate a resource object + * @param sourceSupplier the function called with the returned resource + * object from {@code resourceSupplier} and should return a {@code SingleSource} instance * to be run by the operator - * @param disposer the consumer of the generated resource that is called exactly once for - * that particular resource when the generated SingleSource terminates + * @param resourceCleanup the consumer of the generated resource that is called exactly once for + * that particular resource when the generated {@code SingleSource} terminates * (successfully or with an error) or gets disposed. - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} and {@code resourceCleanup} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public static Single using(Supplier resourceSupplier, - Function> singleFunction, - Consumer disposer) { - return using(resourceSupplier, singleFunction, disposer, true); + @NonNull + public static <@NonNull T, @NonNull U> Single using(@NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup) { + return using(resourceSupplier, sourceSupplier, resourceCleanup, true); } /** - * Allows using and disposing a resource while running a SingleSource instance generated from + * Allows using and disposing a resource while running a {@link SingleSource} instance generated from * that resource (similar to a try-with-resources). *

* @@ -1485,40 +1997,41 @@ public static Single using(Supplier resourceSupplier, *

Scheduler:
*
{@code using} does not operate by default on a particular {@link Scheduler}.
*
- * @param the value type of the SingleSource generated + * @param the value type of the {@code SingleSource} generated * @param the resource type - * @param resourceSupplier the Supplier called for each SingleObserver to generate a resource Object - * @param singleFunction the function called with the returned resource - * Object from {@code resourceSupplier} and should return a SingleSource instance + * @param resourceSupplier the {@link Supplier} called for each {@link SingleObserver} to generate a resource object + * @param sourceSupplier the function called with the returned resource + * object from {@code resourceSupplier} and should return a {@code SingleSource} instance * to be run by the operator - * @param disposer the consumer of the generated resource that is called exactly once for - * that particular resource when the generated SingleSource terminates + * @param resourceCleanup the consumer of the generated resource that is called exactly once for + * that particular resource when the generated {@code SingleSource} terminates * (successfully or with an error) or gets disposed. * @param eager * If {@code true} then resource disposal will happen either on a {@code dispose()} call before the upstream is disposed * or just before the emission of a terminal event ({@code onSuccess} or {@code onError}). * If {@code false} the resource disposal will happen either on a {@code dispose()} call after the upstream is disposed * or just after the emission of a terminal event ({@code onSuccess} or {@code onError}). - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code resourceSupplier}, {@code sourceSupplier} or {@code resourceCleanup} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single using( - final Supplier resourceSupplier, - final Function> singleFunction, - final Consumer disposer, - final boolean eager) { - ObjectHelper.requireNonNull(resourceSupplier, "resourceSupplier is null"); - ObjectHelper.requireNonNull(singleFunction, "singleFunction is null"); - ObjectHelper.requireNonNull(disposer, "disposer is null"); + public static <@NonNull T, @NonNull U> Single using( + @NonNull Supplier resourceSupplier, + @NonNull Function> sourceSupplier, + @NonNull Consumer resourceCleanup, + boolean eager) { + Objects.requireNonNull(resourceSupplier, "resourceSupplier is null"); + Objects.requireNonNull(sourceSupplier, "sourceSupplier is null"); + Objects.requireNonNull(resourceCleanup, "resourceCleanup is null"); - return RxJavaPlugins.onAssembly(new SingleUsing(resourceSupplier, singleFunction, disposer, eager)); + return RxJavaPlugins.onAssembly(new SingleUsing<>(resourceSupplier, sourceSupplier, resourceCleanup, eager)); } /** - * Wraps a SingleSource instance into a new Single instance if not already a Single + * Wraps a {@link SingleSource} instance into a new {@code Single} instance if not already a {@code Single} * instance. *

* @@ -1528,34 +2041,35 @@ public static Single using( *

* @param the value type * @param source the source to wrap - * @return the Single wrapper or the source cast to Single (if possible) + * @return the new {@code Single} instance + * @throws NullPointerException if {@code source} is {@code null} */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single wrap(SingleSource source) { - ObjectHelper.requireNonNull(source, "source is null"); + public static <@NonNull T> Single wrap(@NonNull SingleSource source) { + Objects.requireNonNull(source, "source is null"); if (source instanceof Single) { return RxJavaPlugins.onAssembly((Single)source); } - return RxJavaPlugins.onAssembly(new SingleFromUnsafeSource(source)); + return RxJavaPlugins.onAssembly(new SingleFromUnsafeSource<>(source)); } /** - * Waits until all SingleSource sources provided by the Iterable sequence signal a success + * Waits until all {@link SingleSource} sources provided by the {@link Iterable} sequence signal a success * value and calls a zipper function with an array of these values to return a result - * to be emitted to downstream. + * to be emitted to the downstream. *

- * If the {@code Iterable} of {@link SingleSource}s is empty a {@link NoSuchElementException} error is signalled after subscription. + * If the {@code Iterable} of {@code SingleSource}s is empty a {@link NoSuchElementException} error is signaled after subscription. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. * *

* *

- * If any of the SingleSources signal an error, all other SingleSources get disposed and the + * If any of the {@code SingleSources} signal an error, all other {@code SingleSource}s get disposed and the * error emitted to downstream immediately. *

*
Scheduler:
@@ -1563,452 +2077,468 @@ public static Single wrap(SingleSource source) { *
* @param the common value type * @param the result value type - * @param sources the Iterable sequence of SingleSource instances. An empty sequence will result in an - * {@code onError} signal of {@link NoSuchElementException}. - * @param zipper the function that receives an array with values from each SingleSource + * @param sources the {@code Iterable} sequence of {@code SingleSource} instances. An empty sequence will result in an + * {@code onError} signal of {@code NoSuchElementException}. + * @param zipper the function that receives an array with values from each {@code SingleSource} * and should return a value to be emitted to downstream - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code zipper} or {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single zip(final Iterable> sources, Function zipper) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); - return RxJavaPlugins.onAssembly(new SingleZipIterable(sources, zipper)); + public static <@NonNull T, @NonNull R> Single zip(@NonNull Iterable<@NonNull ? extends SingleSource> sources, + @NonNull Function zipper) { + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); + return RxJavaPlugins.onAssembly(new SingleZipIterable<>(sources, zipper)); } /** - * Returns a Single that emits the results of a specified combiner function applied to two items emitted by - * two other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to two items emitted by + * two other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - BiFunction zipper + public static <@NonNull T1, @NonNull T2, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull BiFunction zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2); } /** - * Returns a Single that emits the results of a specified combiner function applied to three items emitted - * by three other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to three items emitted + * by three other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, - Function3 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, + @NonNull Function3 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3); } /** - * Returns a Single that emits the results of a specified combiner function applied to four items - * emitted by four other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to four items + * emitted by four other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type - * @param the fourth source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type + * @param the fourth source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param source4 - * a fourth source Single + * a fourth source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, {@code source4} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4, - Function4 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4, + @NonNull Function4 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4); } /** - * Returns a Single that emits the results of a specified combiner function applied to five items - * emitted by five other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to five items + * emitted by five other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type - * @param the fourth source Single's value type - * @param the fifth source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type + * @param the fourth source {@code SingleSource}'s value type + * @param the fifth source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param source4 - * a fourth source Single + * a fourth source {@code SingleSource} * @param source5 - * a fifth source Single + * a fifth source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, {@code source4} + * {@code source5} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4, - SingleSource source5, - Function5 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4, + @NonNull SingleSource source5, + @NonNull Function5 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5); } /** - * Returns a Single that emits the results of a specified combiner function applied to six items - * emitted by six other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to six items + * emitted by six other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type - * @param the fourth source Single's value type - * @param the fifth source Single's value type - * @param the sixth source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type + * @param the fourth source {@code SingleSource}'s value type + * @param the fifth source {@code SingleSource}'s value type + * @param the sixth source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param source4 - * a fourth source Single + * a fourth source {@code SingleSource} * @param source5 - * a fifth source Single + * a fifth source {@code SingleSource} * @param source6 - * a sixth source Single + * a sixth source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, {@code source4} + * {@code source5}, {@code source6} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4, - SingleSource source5, SingleSource source6, - Function6 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4, + @NonNull SingleSource source5, @NonNull SingleSource source6, + @NonNull Function6 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6); } /** - * Returns a Single that emits the results of a specified combiner function applied to seven items - * emitted by seven other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to seven items + * emitted by seven other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type - * @param the fourth source Single's value type - * @param the fifth source Single's value type - * @param the sixth source Single's value type - * @param the seventh source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type + * @param the fourth source {@code SingleSource}'s value type + * @param the fifth source {@code SingleSource}'s value type + * @param the sixth source {@code SingleSource}'s value type + * @param the seventh source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param source4 - * a fourth source Single + * a fourth source {@code SingleSource} * @param source5 - * a fifth source Single + * a fifth source {@code SingleSource} * @param source6 - * a sixth source Single + * a sixth source {@code SingleSource} * @param source7 - * a seventh source Single + * a seventh source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, {@code source4} + * {@code source5}, {@code source6}, {@code source7} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4, - SingleSource source5, SingleSource source6, - SingleSource source7, - Function7 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4, + @NonNull SingleSource source5, @NonNull SingleSource source6, + @NonNull SingleSource source7, + @NonNull Function7 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6, source7); } /** - * Returns a Single that emits the results of a specified combiner function applied to eight items - * emitted by eight other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to eight items + * emitted by eight other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type - * @param the fourth source Single's value type - * @param the fifth source Single's value type - * @param the sixth source Single's value type - * @param the seventh source Single's value type - * @param the eighth source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type + * @param the fourth source {@code SingleSource}'s value type + * @param the fifth source {@code SingleSource}'s value type + * @param the sixth source {@code SingleSource}'s value type + * @param the seventh source {@code SingleSource}'s value type + * @param the eighth source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param source4 - * a fourth source Single + * a fourth source {@code SingleSource} * @param source5 - * a fifth source Single + * a fifth source {@code SingleSource} * @param source6 - * a sixth source Single + * a sixth source {@code SingleSource} * @param source7 - * a seventh source Single + * a seventh source {@code SingleSource} * @param source8 - * an eighth source Single + * an eighth source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, {@code source4} + * {@code source5}, {@code source6}, {@code source7}, {@code source8} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4, - SingleSource source5, SingleSource source6, - SingleSource source7, SingleSource source8, - Function8 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4, + @NonNull SingleSource source5, @NonNull SingleSource source6, + @NonNull SingleSource source7, @NonNull SingleSource source8, + @NonNull Function8 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6, source7, source8); } /** - * Returns a Single that emits the results of a specified combiner function applied to nine items - * emitted by nine other Singles. + * Returns a {@code Single} that emits the results of a specified combiner function applied to nine items + * emitted by nine other {@link SingleSource}s. *

- * + * *

*
Scheduler:
*
{@code zip} does not operate by default on a particular {@link Scheduler}.
*
* - * @param the first source Single's value type - * @param the second source Single's value type - * @param the third source Single's value type - * @param the fourth source Single's value type - * @param the fifth source Single's value type - * @param the sixth source Single's value type - * @param the seventh source Single's value type - * @param the eighth source Single's value type - * @param the ninth source Single's value type + * @param the first source {@code SingleSource}'s value type + * @param the second source {@code SingleSource}'s value type + * @param the third source {@code SingleSource}'s value type + * @param the fourth source {@code SingleSource}'s value type + * @param the fifth source {@code SingleSource}'s value type + * @param the sixth source {@code SingleSource}'s value type + * @param the seventh source {@code SingleSource}'s value type + * @param the eighth source {@code SingleSource}'s value type + * @param the ninth source {@code SingleSource}'s value type * @param the result value type * @param source1 - * the first source Single + * the first source {@code SingleSource} * @param source2 - * a second source Single + * a second source {@code SingleSource} * @param source3 - * a third source Single + * a third source {@code SingleSource} * @param source4 - * a fourth source Single + * a fourth source {@code SingleSource} * @param source5 - * a fifth source Single + * a fifth source {@code SingleSource} * @param source6 - * a sixth source Single + * a sixth source {@code SingleSource} * @param source7 - * a seventh source Single + * a seventh source {@code SingleSource} * @param source8 - * an eighth source Single + * an eighth source {@code SingleSource} * @param source9 - * a ninth source Single + * a ninth source {@code SingleSource} * @param zipper - * a function that, when applied to the item emitted by each of the source Singles, results in an - * item that will be emitted by the resulting Single - * @return a Single that emits the zipped results + * a function that, when applied to the item emitted by each of the source {@code SingleSource}s, results in an + * item that will be emitted by the resulting {@code Single} + * @return the new {@code Single} that emits the zipped results + * @throws NullPointerException if {@code source1}, {@code source2}, {@code source3}, {@code source4} + * {@code source5}, {@code source6}, {@code source7}, {@code source8}, + * {@code source9} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public static Single zip( - SingleSource source1, SingleSource source2, - SingleSource source3, SingleSource source4, - SingleSource source5, SingleSource source6, - SingleSource source7, SingleSource source8, - SingleSource source9, - Function9 zipper + public static <@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> Single zip( + @NonNull SingleSource source1, @NonNull SingleSource source2, + @NonNull SingleSource source3, @NonNull SingleSource source4, + @NonNull SingleSource source5, @NonNull SingleSource source6, + @NonNull SingleSource source7, @NonNull SingleSource source8, + @NonNull SingleSource source9, + @NonNull Function9 zipper ) { - ObjectHelper.requireNonNull(source1, "source1 is null"); - ObjectHelper.requireNonNull(source2, "source2 is null"); - ObjectHelper.requireNonNull(source3, "source3 is null"); - ObjectHelper.requireNonNull(source4, "source4 is null"); - ObjectHelper.requireNonNull(source5, "source5 is null"); - ObjectHelper.requireNonNull(source6, "source6 is null"); - ObjectHelper.requireNonNull(source7, "source7 is null"); - ObjectHelper.requireNonNull(source8, "source8 is null"); - ObjectHelper.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(source1, "source1 is null"); + Objects.requireNonNull(source2, "source2 is null"); + Objects.requireNonNull(source3, "source3 is null"); + Objects.requireNonNull(source4, "source4 is null"); + Objects.requireNonNull(source5, "source5 is null"); + Objects.requireNonNull(source6, "source6 is null"); + Objects.requireNonNull(source7, "source7 is null"); + Objects.requireNonNull(source8, "source8 is null"); + Objects.requireNonNull(source9, "source9 is null"); + Objects.requireNonNull(zipper, "zipper is null"); return zipArray(Functions.toFunction(zipper), source1, source2, source3, source4, source5, source6, source7, source8, source9); } /** - * Waits until all SingleSource sources provided via an array signal a success + * Waits until all {@link SingleSource} sources provided via an array signal a success * value and calls a zipper function with an array of these values to return a result * to be emitted to downstream. *

* *

- * If the array of {@link SingleSource}s is empty a {@link NoSuchElementException} error is signalled immediately. + * If the array of {@code SingleSource}s is empty a {@link NoSuchElementException} error is signaled immediately. *

* Note on method signature: since Java doesn't allow creating a generic array with {@code new T[]}, the * implementation of this operator has to create an {@code Object[]} instead. Unfortunately, a - * {@code Function} passed to the method would trigger a {@code ClassCastException}. + * {@code Function} passed to the method would trigger a {@link ClassCastException}. *

- * If any of the SingleSources signal an error, all other SingleSources get disposed and the + * If any of the {@code SingleSource}s signal an error, all other {@code SingleSource}s get disposed and the * error emitted to downstream immediately. *

*
Scheduler:
@@ -2016,74 +2546,77 @@ public static Single zip( *
* @param the common value type * @param the result value type - * @param sources the array of SingleSource instances. An empty sequence will result in an - * {@code onError} signal of {@link NoSuchElementException}. - * @param zipper the function that receives an array with values from each SingleSource + * @param sources the array of {@code SingleSource} instances. An empty sequence will result in an + * {@code onError} signal of {@code NoSuchElementException}. + * @param zipper the function that receives an array with values from each {@code SingleSource} * and should return a value to be emitted to downstream - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code zipper} or {@code sources} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public static Single zipArray(Function zipper, SingleSource... sources) { - ObjectHelper.requireNonNull(zipper, "zipper is null"); - ObjectHelper.requireNonNull(sources, "sources is null"); + @SafeVarargs + public static <@NonNull T, @NonNull R> Single zipArray(@NonNull Function zipper, @NonNull SingleSource... sources) { + Objects.requireNonNull(zipper, "zipper is null"); + Objects.requireNonNull(sources, "sources is null"); if (sources.length == 0) { return error(new NoSuchElementException()); } - return RxJavaPlugins.onAssembly(new SingleZipArray(sources, zipper)); + return RxJavaPlugins.onAssembly(new SingleZipArray<>(sources, zipper)); } /** - * Signals the event of this or the other SingleSource whichever signals first. + * Signals the event of this or the other {@link SingleSource} whichever signals first. *

- * + * *

*
Scheduler:
*
{@code ambWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param other the other SingleSource to race for the first emission of success or error - * @return the new Single instance. A subscription to this provided source will occur after subscribing + * @param other the other {@code SingleSource} to race for the first emission of success or error + * @return the new {@code Single} instance. A subscription to this provided source will occur after subscribing * to the current source. + * @throws NullPointerException if {@code other} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - @SuppressWarnings("unchecked") - public final Single ambWith(SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Single ambWith(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); return ambArray(this, other); } /** - * Hides the identity of the current Single, including the Disposable that is sent + * Hides the identity of the current {@code Single}, including the {@link Disposable} that is sent * to the downstream via {@code onSubscribe()}. *

- * + * *

*
Scheduler:
*
{@code hide} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Single instance + * @return the new {@code Single} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single hide() { - return RxJavaPlugins.onAssembly(new SingleHide(this)); + return RxJavaPlugins.onAssembly(new SingleHide<>(this)); } /** - * Transform a Single by applying a particular Transformer function to it. + * Transform a {@code Single} by applying a particular {@link SingleTransformer} function to it. *

- * + * *

- * This method operates on the Single itself whereas {@link #lift} operates on the Single's SingleObservers. + * This method operates on the {@code Single} itself whereas {@link #lift} operates on {@link SingleObserver}s. *

- * If the operator you are creating is designed to act on the individual item emitted by a Single, use - * {@link #lift}. If your operator is designed to transform the source Single as a whole (for instance, by + * If the operator you are creating is designed to act on the individual item emitted by a {@code Single}, use + * {@link #lift}. If your operator is designed to transform the current {@code Single} as a whole (for instance, by * applying a particular set of existing RxJava operators to it) use {@code compose}. *

*
Scheduler:
@@ -2091,40 +2624,43 @@ public final Single hide() { *
* * @param the value type of the single returned by the transformer function - * @param transformer the transformer function, not null - * @return the source Single, transformed by the transformer function + * @param transformer the transformer function, not {@code null} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code transformer} is {@code null} * @see RxJava wiki: Implementing Your Own Operators */ @SuppressWarnings("unchecked") @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single compose(SingleTransformer transformer) { - return wrap(((SingleTransformer) ObjectHelper.requireNonNull(transformer, "transformer is null")).apply(this)); + @NonNull + public final <@NonNull R> Single compose(@NonNull SingleTransformer transformer) { + return wrap(((SingleTransformer) Objects.requireNonNull(transformer, "transformer is null")).apply(this)); } /** - * Stores the success value or exception from the current Single and replays it to late SingleObservers. + * Stores the success value or exception from the current {@code Single} and replays it to late {@link SingleObserver}s. *

* *

- * The returned Single subscribes to the current Single when the first SingleObserver subscribes. + * The returned {@code Single} subscribes to the current {@code Single} when the first {@code SingleObserver} subscribes. *

*
Scheduler:
*
{@code cache} does not operate by default on a particular {@link Scheduler}.
*
* - * @return the new Single instance + * @return the new {@code Single} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single cache() { - return RxJavaPlugins.onAssembly(new SingleCache(this)); + return RxJavaPlugins.onAssembly(new SingleCache<>(this)); } /** - * Casts the success value of the current Single into the target type or signals a - * ClassCastException if not compatible. + * Casts the success value of the current {@code Single} into the target type or signals a + * {@link ClassCastException} if not compatible. *

* *

@@ -2132,23 +2668,106 @@ public final Single cache() { *
{@code cast} does not operate by default on a particular {@link Scheduler}.
*
* @param the target type - * @param clazz the type token to use for casting the success result from the current Single - * @return the new Single instance + * @param clazz the type token to use for casting the success result from the current {@code Single} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code clazz} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single cast(final Class clazz) { - ObjectHelper.requireNonNull(clazz, "clazz is null"); + public final <@NonNull U> Single cast(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); return map(Functions.castFunction(clazz)); } /** - * Returns a Flowable that emits the item emitted by the source Single, then the item emitted by the - * specified Single. + * Returns a {@code Single} that is based on applying a specified function to the item emitted by the current {@code Single}, + * where that function returns a {@link SingleSource}. + *

+ * + *

+ * The operator is an alias for {@link #flatMap(Function)} + *

+ *
Scheduler:
+ *
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the result value type + * @param mapper + * a function that, when applied to the item emitted by the current {@code Single}, returns a {@code SingleSource} + * @return the new {@code Single} returned from {@code mapper} when applied to the item emitted by the current {@code Single} + * @throws NullPointerException if {@code mapper} is {@code null} + * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> Single concatMap(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMap<>(this, mapper)); + } + + /** + * Returns a {@link Completable} that completes based on applying a specified function to the item emitted by the + * current {@code Single}, where that function returns a {@link CompletableSource}. + *

+ * + *

+ * The operator is an alias for {@link #flatMapCompletable(Function)}. + *

+ *
Scheduler:
+ *
{@code concatMapCompletable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param mapper + * a function that, when applied to the item emitted by the current {@code Single}, returns a + * {@code CompletableSource} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Completable concatMapCompletable(@NonNull Function mapper) { + return flatMapCompletable(mapper); + } + + /** + * Returns a {@link Maybe} that is based on applying a specified function to the item emitted by the current {@code Single}, + * where that function returns a {@link MaybeSource}. + *

+ * + *

+ * The operator is an alias for {@link #flatMapMaybe(Function)}. + *

+ *
Scheduler:
+ *
{@code concatMapMaybe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the result value type + * @param mapper + * a function that, when applied to the item emitted by the current {@code Single}, returns a {@code MaybeSource} + * @return the new {@code Maybe} returned from {@code mapper} when applied to the item emitted by the current {@code Single} + * @throws NullPointerException if {@code mapper} is {@code null} + * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> Maybe concatMapMaybe(@NonNull Function> mapper) { + return flatMapMaybe(mapper); + } + + /** + * Returns a {@link Flowable} that emits the item emitted by the current {@code Single}, then the item emitted by the + * specified {@link SingleSource}. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -2157,23 +2776,25 @@ public final Single cast(final Class clazz) { *
* * @param other - * a Single to be concatenated after the current - * @return a Flowable that emits the item emitted by the source Single, followed by the item emitted by - * {@code t1} + * a {@code SingleSource} to be concatenated after the current + * @return the new {@code Flowable} that emits the item emitted by the current {@code Single}, followed by the item emitted by + * {@code other} + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Concat */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable concatWith(SingleSource other) { + @NonNull + public final Flowable concatWith(@NonNull SingleSource other) { return concat(this, other); } /** - * Delays the emission of the success signal from the current Single by the specified amount. + * Delays the emission of the success signal from the current {@code Single} by the specified amount. * An error signal will not be delayed. *

- * + * *

*
Scheduler:
*
{@code delay} operates by default on the {@code computation} {@link Scheduler}.
@@ -2181,19 +2802,22 @@ public final Flowable concatWith(SingleSource other) { * * @param time the amount of time the success signal should be delayed for * @param unit the time unit - * @return the new Single instance + * @return the new {@code Single} instance * @since 2.0 + * @throws NullPointerException if {@code unit} is {@code null} + * @see #delay(long, TimeUnit, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Single delay(long time, TimeUnit unit) { + @NonNull + public final Single delay(long time, @NonNull TimeUnit unit) { return delay(time, unit, Schedulers.computation(), false); } /** - * Delays the emission of the success or error signal from the current Single by the specified amount. + * Delays the emission of the success or error signal from the current {@code Single} by the specified amount. *

- * + * *

*
Scheduler:
*
{@code delay} operates by default on the {@code computation} {@link Scheduler}.
@@ -2201,21 +2825,23 @@ public final Single delay(long time, TimeUnit unit) { *

History: 2.1.5 - experimental * @param time the amount of time the success or error signal should be delayed for * @param unit the time unit - * @param delayError if true, both success and error signals are delayed. if false, only success signals are delayed. - * @return the new Single instance + * @param delayError if {@code true}, both success and error signals are delayed. if {@code false}, only success signals are delayed. + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Single delay(long time, TimeUnit unit, boolean delayError) { + @NonNull + public final Single delay(long time, @NonNull TimeUnit unit, boolean delayError) { return delay(time, unit, Schedulers.computation(), delayError); } /** - * Delays the emission of the success signal from the current Single by the specified amount. + * Delays the emission of the success signal from the current {@code Single} by the specified amount. * An error signal will not be delayed. *

- * + * *

*
Scheduler:
*
you specify the {@link Scheduler} where the non-blocking wait and emission happens
@@ -2224,22 +2850,24 @@ public final Single delay(long time, TimeUnit unit, boolean delayError) { * @param time the amount of time the success signal should be delayed for * @param unit the time unit * @param scheduler the target scheduler to use for the non-blocking wait and emission - * @return the new Single instance + * @return the new {@code Single} instance * @throws NullPointerException - * if unit is null, or - * if scheduler is null + * if {@code unit} is {@code null}, or + * if {@code scheduler} is {@code null} * @since 2.0 + * @see #delay(long, TimeUnit, Scheduler, boolean) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single delay(final long time, final TimeUnit unit, final Scheduler scheduler) { + @NonNull + public final Single delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return delay(time, unit, scheduler, false); } /** - * Delays the emission of the success or error signal from the current Single by the specified amount. + * Delays the emission of the success or error signal from the current {@code Single} by the specified amount. *

- * + * *

*
Scheduler:
*
you specify the {@link Scheduler} where the non-blocking wait and emission happens
@@ -2248,104 +2876,107 @@ public final Single delay(final long time, final TimeUnit unit, final Schedul * @param time the amount of time the success or error signal should be delayed for * @param unit the time unit * @param scheduler the target scheduler to use for the non-blocking wait and emission - * @param delayError if true, both success and error signals are delayed. if false, only success signals are delayed. - * @return the new Single instance + * @param delayError if {@code true}, both success and error signals are delayed. if {@code false}, only success signals are delayed. + * @return the new {@code Single} instance * @throws NullPointerException - * if unit is null, or - * if scheduler is null + * if {@code unit} is {@code null}, or + * if {@code scheduler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single delay(final long time, final TimeUnit unit, final Scheduler scheduler, boolean delayError) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new SingleDelay(this, time, unit, scheduler, delayError)); + public final Single delay(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, boolean delayError) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleDelay<>(this, time, unit, scheduler, delayError)); } /** - * Delays the actual subscription to the current Single until the given other CompletableSource + * Delays the actual subscription to the current {@code Single} until the given other {@link CompletableSource} * completes. *

* *

If the delaying source signals an error, that error is re-emitted and no subscription - * to the current Single happens. + * to the current {@code Single} happens. *

*
Scheduler:
*
{@code delaySubscription} does not operate by default on a particular {@link Scheduler}.
*
- * @param other the CompletableSource that has to complete before the subscription to the - * current Single happens - * @return the new Single instance + * @param subscriptionIndicator the {@code CompletableSource} that has to complete before the subscription to the + * current {@code Single} happens + * @return the new {@code Single} instance + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single delaySubscription(CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new SingleDelayWithCompletable(this, other)); + public final Single delaySubscription(@NonNull CompletableSource subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new SingleDelayWithCompletable<>(this, subscriptionIndicator)); } /** - * Delays the actual subscription to the current Single until the given other SingleSource + * Delays the actual subscription to the current {@code Single} until the given other {@link SingleSource} * signals success. *

* *

If the delaying source signals an error, that error is re-emitted and no subscription - * to the current Single happens. + * to the current {@code Single} happens. *

*
Scheduler:
*
{@code delaySubscription} does not operate by default on a particular {@link Scheduler}.
*
* @param the element type of the other source - * @param other the SingleSource that has to complete before the subscription to the - * current Single happens - * @return the new Single instance + * @param subscriptionIndicator the {@code SingleSource} that has to complete before the subscription to the + * current {@code Single} happens + * @return the new {@code Single} instance + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single delaySubscription(SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new SingleDelayWithSingle(this, other)); + public final <@NonNull U> Single delaySubscription(@NonNull SingleSource subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new SingleDelayWithSingle<>(this, subscriptionIndicator)); } /** - * Delays the actual subscription to the current Single until the given other ObservableSource + * Delays the actual subscription to the current {@code Single} until the given other {@link ObservableSource} * signals its first value or completes. *

* *

If the delaying source signals an error, that error is re-emitted and no subscription - * to the current Single happens. + * to the current {@code Single} happens. *

*
Scheduler:
*
{@code delaySubscription} does not operate by default on a particular {@link Scheduler}.
*
* @param the element type of the other source - * @param other the ObservableSource that has to signal a value or complete before the - * subscription to the current Single happens - * @return the new Single instance + * @param subscriptionIndicator the {@code ObservableSource} that has to signal a value or complete before the + * subscription to the current {@code Single} happens + * @return the new {@code Single} instance + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single delaySubscription(ObservableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new SingleDelayWithObservable(this, other)); + public final <@NonNull U> Single delaySubscription(@NonNull ObservableSource subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new SingleDelayWithObservable<>(this, subscriptionIndicator)); } /** - * Delays the actual subscription to the current Single until the given other Publisher + * Delays the actual subscription to the current {@code Single} until the given other {@link Publisher} * signals its first value or completes. *

* *

If the delaying source signals an error, that error is re-emitted and no subscription - * to the current Single happens. - *

The other source is consumed in an unbounded manner (requesting Long.MAX_VALUE from it). + * to the current {@code Single} happens. + *

The other source is consumed in an unbounded manner (requesting {@link Long#MAX_VALUE} from it). *

*
Backpressure:
*
The {@code other} publisher is consumed in an unbounded fashion but will be @@ -2354,63 +2985,68 @@ public final Single delaySubscription(ObservableSource other) { *
{@code delaySubscription} does not operate by default on a particular {@link Scheduler}.
*
* @param the element type of the other source - * @param other the Publisher that has to signal a value or complete before the - * subscription to the current Single happens - * @return the new Single instance + * @param subscriptionIndicator the {@code Publisher} that has to signal a value or complete before the + * subscription to the current {@code Single} happens + * @return the new {@code Single} instance + * @throws NullPointerException if {@code subscriptionIndicator} is {@code null} * @since 2.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single delaySubscription(Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new SingleDelayWithPublisher(this, other)); + public final <@NonNull U> Single delaySubscription(@NonNull Publisher subscriptionIndicator) { + Objects.requireNonNull(subscriptionIndicator, "subscriptionIndicator is null"); + return RxJavaPlugins.onAssembly(new SingleDelayWithPublisher<>(this, subscriptionIndicator)); } /** - * Delays the actual subscription to the current Single until the given time delay elapsed. + * Delays the actual subscription to the current {@code Single} until the given time delay elapsed. *

* *

*
Scheduler:
- *
{@code delaySubscription} does by default subscribe to the current Single + *
{@code delaySubscription} does by default subscribe to the current {@code Single} * on the {@code computation} {@link Scheduler} after the delay.
*
* @param time the time amount to wait with the subscription * @param unit the time unit of the waiting - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Single delaySubscription(long time, TimeUnit unit) { + @NonNull + public final Single delaySubscription(long time, @NonNull TimeUnit unit) { return delaySubscription(time, unit, Schedulers.computation()); } /** - * Delays the actual subscription to the current Single until the given time delay elapsed. + * Delays the actual subscription to the current {@code Single} until the given time delay elapsed. *

* *

*
Scheduler:
- *
{@code delaySubscription} does by default subscribe to the current Single + *
{@code delaySubscription} does by default subscribe to the current {@code Single} * on the {@link Scheduler} you provided, after the delay.
*
* @param time the time amount to wait with the subscription * @param unit the time unit of the waiting - * @param scheduler the scheduler to wait on and subscribe on to the current Single - * @return the new Single instance + * @param scheduler the {@code Scheduler} to wait on and subscribe on to the current {@code Single} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single delaySubscription(long time, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Single delaySubscription(long time, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return delaySubscription(Observable.timer(time, unit, scheduler)); } /** - * Maps the {@link Notification} success value of this Single back into normal + * Maps the {@link Notification} success value of the current {@code Single} back into normal * {@code onSuccess}, {@code onError} or {@code onComplete} signals as a * {@link Maybe} source. *

@@ -2436,23 +3072,24 @@ public final Single delaySubscription(long time, TimeUnit unit, Scheduler sch *

History: 2.2.4 - experimental * @param the result type * @param selector the function called with the success item and should - * return a {@link Notification} instance. - * @return the new Maybe instance + * return a {@code Notification} instance. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code selector} is {@code null} * @since 3.0.0 * @see #materialize() */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe dematerialize(Function> selector) { - ObjectHelper.requireNonNull(selector, "selector is null"); - return RxJavaPlugins.onAssembly(new SingleDematerialize(this, selector)); + public final <@NonNull R> Maybe dematerialize(@NonNull Function> selector) { + Objects.requireNonNull(selector, "selector is null"); + return RxJavaPlugins.onAssembly(new SingleDematerialize<>(this, selector)); } /** * Calls the specified consumer with the success item after this item has been emitted to the downstream. *

- * + * *

* Note that the {@code doAfterSuccess} action is shared between subscriptions and as such * should be thread-safe. @@ -2461,22 +3098,23 @@ public final Maybe dematerialize(Function> sel *

{@code doAfterSuccess} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.1 - experimental - * @param onAfterSuccess the Consumer that will be called after emitting an item from upstream to the downstream - * @return the new Single instance + * @param onAfterSuccess the {@link Consumer} that will be called after emitting an item from upstream to the downstream + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onAfterSuccess} is {@code null} * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doAfterSuccess(Consumer onAfterSuccess) { - ObjectHelper.requireNonNull(onAfterSuccess, "onAfterSuccess is null"); - return RxJavaPlugins.onAssembly(new SingleDoAfterSuccess(this, onAfterSuccess)); + public final Single doAfterSuccess(@NonNull Consumer onAfterSuccess) { + Objects.requireNonNull(onAfterSuccess, "onAfterSuccess is null"); + return RxJavaPlugins.onAssembly(new SingleDoAfterSuccess<>(this, onAfterSuccess)); } /** - * Registers an {@link Action} to be called after this Single invokes either onSuccess or onError. + * Registers an {@link Action} to be called after this {@code Single} invokes either {@code onSuccess} or {@code onError}. *

- * + * *

* Note that the {@code doAfterTerminate} action is shared between subscriptions and as such * should be thread-safe.

@@ -2488,74 +3126,105 @@ public final Single doAfterSuccess(Consumer onAfterSuccess) { * *

History: 2.0.6 - experimental * @param onAfterTerminate - * an {@link Action} to be invoked when the source Single finishes - * @return a Single that emits the same items as the source Single, then invokes the - * {@link Action} + * an {@code Action} to be invoked when the current {@code Single} finishes + * @return the new {@code Single} that emits the same items as the current {@code Single}, then invokes the + * {@code Action} + * @throws NullPointerException if {@code onAfterTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doAfterTerminate(Action onAfterTerminate) { - ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); - return RxJavaPlugins.onAssembly(new SingleDoAfterTerminate(this, onAfterTerminate)); + public final Single doAfterTerminate(@NonNull Action onAfterTerminate) { + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); + return RxJavaPlugins.onAssembly(new SingleDoAfterTerminate<>(this, onAfterTerminate)); } /** - * Calls the specified action after this Single signals onSuccess or onError or gets disposed by + * Calls the specified action after this {@code Single} signals {@code onSuccess} or {@code onError} or gets disposed by * the downstream. *

In case of a race between a terminal event and a dispose call, the provided {@code onFinally} action * is executed once per subscription. *

Note that the {@code onFinally} action is shared between subscriptions and as such * should be thread-safe. *

- * + * *

*
*
Scheduler:
*
{@code doFinally} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.1 - experimental - * @param onFinally the action called when this Single terminates or gets disposed - * @return the new Single instance + * @param onFinally the action called when this {@code Single} terminates or gets disposed + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onFinally} is {@code null} * @since 2.1 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doFinally(Action onFinally) { - ObjectHelper.requireNonNull(onFinally, "onFinally is null"); - return RxJavaPlugins.onAssembly(new SingleDoFinally(this, onFinally)); + public final Single doFinally(@NonNull Action onFinally) { + Objects.requireNonNull(onFinally, "onFinally is null"); + return RxJavaPlugins.onAssembly(new SingleDoFinally<>(this, onFinally)); + } + + /** + * Calls the appropriate {@code onXXX} method (shared between all {@link SingleObserver}s) for the lifecycle events of + * the sequence (subscription, disposal). + *

+ * + *

+ *
Scheduler:
+ *
{@code doOnLifecycle} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param onSubscribe + * a {@link Consumer} called with the {@link Disposable} sent via {@link SingleObserver#onSubscribe(Disposable)} + * @param onDispose + * called when the downstream disposes the {@code Disposable} via {@code dispose()} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onSubscribe} or {@code onDispose} is {@code null} + * @see ReactiveX operators documentation: Do + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Single doOnLifecycle(@NonNull Consumer onSubscribe, @NonNull Action onDispose) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + Objects.requireNonNull(onDispose, "onDispose is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnLifecycle<>(this, onSubscribe, onDispose)); } /** - * Calls the shared consumer with the Disposable sent through the onSubscribe for each - * SingleObserver that subscribes to the current Single. + * Calls the shared consumer with the {@link Disposable} sent through the {@code onSubscribe} for each + * {@link SingleObserver} that subscribes to the current {@code Single}. *

- * + * *

*
*
Scheduler:
*
{@code doOnSubscribe} does not operate by default on a particular {@link Scheduler}.
*
- * @param onSubscribe the consumer called with the Disposable sent via onSubscribe - * @return the new Single instance + * @param onSubscribe the consumer called with the {@code Disposable} sent via {@code onSubscribe} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doOnSubscribe(final Consumer onSubscribe) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - return RxJavaPlugins.onAssembly(new SingleDoOnSubscribe(this, onSubscribe)); + public final Single doOnSubscribe(@NonNull Consumer onSubscribe) { + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnSubscribe<>(this, onSubscribe)); } /** - * Returns a Single instance that calls the given onTerminate callback - * just before this Single completes normally or with an exception. + * Returns a {@code Single} instance that calls the given {@code onTerminate} callback + * just before this {@code Single} completes normally or with an exception. *

- * + * *

* This differs from {@code doAfterTerminate} in that this happens before the {@code onSuccess} or * {@code onError} notification. @@ -2565,7 +3234,8 @@ public final Single doOnSubscribe(final Consumer onSubscr *

*

History: 2.2.7 - experimental * @param onTerminate the action to invoke when the consumer calls {@code onSuccess} or {@code onError} - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onTerminate} is {@code null} * @see ReactiveX operators documentation: Do * @see #doOnTerminate(Action) * @since 3.0.0 @@ -2573,36 +3243,37 @@ public final Single doOnSubscribe(final Consumer onSubscr @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doOnTerminate(final Action onTerminate) { - ObjectHelper.requireNonNull(onTerminate, "onTerminate is null"); - return RxJavaPlugins.onAssembly(new SingleDoOnTerminate(this, onTerminate)); + public final Single doOnTerminate(@NonNull Action onTerminate) { + Objects.requireNonNull(onTerminate, "onTerminate is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnTerminate<>(this, onTerminate)); } /** - * Calls the shared consumer with the success value sent via onSuccess for each - * SingleObserver that subscribes to the current Single. + * Calls the shared consumer with the success value sent via {@code onSuccess} for each + * {@link SingleObserver} that subscribes to the current {@code Single}. *

- * + * *

*
*
Scheduler:
*
{@code doOnSuccess} does not operate by default on a particular {@link Scheduler}.
*
- * @param onSuccess the consumer called with the success value of onSuccess - * @return the new Single instance + * @param onSuccess the consumer called with the success value of {@code onSuccess} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onSuccess} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doOnSuccess(final Consumer onSuccess) { - ObjectHelper.requireNonNull(onSuccess, "onSuccess is null"); - return RxJavaPlugins.onAssembly(new SingleDoOnSuccess(this, onSuccess)); + public final Single doOnSuccess(@NonNull Consumer onSuccess) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnSuccess<>(this, onSuccess)); } /** - * Calls the shared consumer with the error sent via onError or the value - * via onSuccess for each SingleObserver that subscribes to the current Single. + * Calls the shared consumer with the error sent via {@code onError} or the value + * via {@code onSuccess} for each {@link SingleObserver} that subscribes to the current {@code Single}. *

* *

@@ -2610,92 +3281,95 @@ public final Single doOnSuccess(final Consumer onSuccess) { *
{@code doOnEvent} does not operate by default on a particular {@link Scheduler}.
*
* @param onEvent the consumer called with the success value of onEvent - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onEvent} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doOnEvent(final BiConsumer onEvent) { - ObjectHelper.requireNonNull(onEvent, "onEvent is null"); - return RxJavaPlugins.onAssembly(new SingleDoOnEvent(this, onEvent)); + public final Single doOnEvent(@NonNull BiConsumer<@Nullable ? super T, @Nullable ? super Throwable> onEvent) { + Objects.requireNonNull(onEvent, "onEvent is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnEvent<>(this, onEvent)); } /** - * Calls the shared consumer with the error sent via onError for each - * SingleObserver that subscribes to the current Single. + * Calls the shared consumer with the error sent via {@code onError} for each + * {@link SingleObserver} that subscribes to the current {@code Single}. *

- * + * *

*
*
Scheduler:
*
{@code doOnError} does not operate by default on a particular {@link Scheduler}.
*
- * @param onError the consumer called with the success value of onError - * @return the new Single instance + * @param onError the consumer called with the success value of {@code onError} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onError} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doOnError(final Consumer onError) { - ObjectHelper.requireNonNull(onError, "onError is null"); - return RxJavaPlugins.onAssembly(new SingleDoOnError(this, onError)); + public final Single doOnError(@NonNull Consumer onError) { + Objects.requireNonNull(onError, "onError is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnError<>(this, onError)); } /** - * Calls the shared {@code Action} if a SingleObserver subscribed to the current Single - * disposes the common Disposable it received via onSubscribe. + * Calls the shared {@link Action} if a {@link SingleObserver} subscribed to the current {@code Single} + * disposes the common {@link Disposable} it received via {@code onSubscribe}. *

- * + * *

*
*
Scheduler:
*
{@code doOnDispose} does not operate by default on a particular {@link Scheduler}.
*
* @param onDispose the action called when the subscription is disposed - * @return the new Single instance - * @throws NullPointerException if onDispose is null + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onDispose} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single doOnDispose(final Action onDispose) { - ObjectHelper.requireNonNull(onDispose, "onDispose is null"); - return RxJavaPlugins.onAssembly(new SingleDoOnDispose(this, onDispose)); + public final Single doOnDispose(@NonNull Action onDispose) { + Objects.requireNonNull(onDispose, "onDispose is null"); + return RxJavaPlugins.onAssembly(new SingleDoOnDispose<>(this, onDispose)); } /** - * Filters the success item of the Single via a predicate function and emitting it if the predicate - * returns true, completing otherwise. + * Filters the success item of the {@code Single} via a predicate function and emitting it if the predicate + * returns {@code true}, completing otherwise. *

- * + * *

*
Scheduler:
*
{@code filter} does not operate by default on a particular {@link Scheduler}.
*
* * @param predicate - * a function that evaluates the item emitted by the source Maybe, returning {@code true} + * a function that evaluates the item emitted by the current {@code Single}, returning {@code true} * if it passes the filter - * @return a Maybe that emit the item emitted by the source Maybe that the filter + * @return the new {@link Maybe} that emit the item emitted by the current {@code Single} that the filter * evaluates as {@code true} + * @throws NullPointerException if {@code predicate} is {@code null} * @see ReactiveX operators documentation: Filter */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe filter(Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate is null"); - return RxJavaPlugins.onAssembly(new MaybeFilterSingle(this, predicate)); + public final Maybe filter(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new MaybeFilterSingle<>(this, predicate)); } /** - * Returns a Single that is based on applying a specified function to the item emitted by the source Single, - * where that function returns a SingleSource. + * Returns a {@code Single} that is based on applying a specified function to the item emitted by the current {@code Single}, + * where that function returns a {@link SingleSource}. *

- * + * *

*
Scheduler:
*
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
@@ -2703,21 +3377,88 @@ public final Maybe filter(Predicate predicate) { * * @param the result value type * @param mapper - * a function that, when applied to the item emitted by the source Single, returns a SingleSource - * @return the Single returned from {@code mapper} when applied to the item emitted by the source Single + * a function that, when applied to the item emitted by the current {@code Single}, returns a {@code SingleSource} + * @return the new {@code Single} returned from {@code mapper} when applied to the item emitted by the current {@code Single} + * @throws NullPointerException if {@code mapper} is {@code null} + * @see ReactiveX operators documentation: FlatMap + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> Single flatMap(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMap<>(this, mapper)); + } + + /** + * Returns a {@code Single} that emits the results of a specified function to the pair of values emitted by the + * current {@code Single} and a specified mapped {@link SingleSource}. + *

+ * + *

+ *
Scheduler:
+ *
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param + * the type of items emitted by the {@code SingleSource} returned by the {@code mapper} function + * @param + * the type of items emitted by the resulting {@code Single} + * @param mapper + * a function that returns a {@code SingleSource} for the item emitted by the current {@code Single} + * @param combiner + * a function that combines one item emitted by each of the source and collection {@code SingleSource} and + * returns an item to be emitted by the resulting {@code SingleSource} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code mapper} or {@code combiner} is {@code null} + * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull U, @NonNull R> Single flatMap(@NonNull Function> mapper, + @NonNull BiFunction combiner) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(combiner, "combiner is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapBiSelector<>(this, mapper, combiner)); + } + + /** + * Maps the {@code onSuccess} or {@code onError} signals of the current {@code Single} into a {@link SingleSource} and emits that + * {@code SingleSource}'s signals. + *

+ * + *

+ *
Scheduler:
+ *
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param + * the result type + * @param onSuccessMapper + * a function that returns a {@code SingleSource} to merge for the {@code onSuccess} item emitted by this {@code Single} + * @param onErrorMapper + * a function that returns a {@code SingleSource} to merge for an {@code onError} notification from this {@code Single} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code onSuccessMapper} or {@code onErrorMapper} is {@code null} * @see ReactiveX operators documentation: FlatMap + * @since 3.0.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single flatMap(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMap(this, mapper)); + public final <@NonNull R> Single flatMap( + @NonNull Function> onSuccessMapper, + @NonNull Function> onErrorMapper) { + Objects.requireNonNull(onSuccessMapper, "onSuccessMapper is null"); + Objects.requireNonNull(onErrorMapper, "onErrorMapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapNotification<>(this, onSuccessMapper, onErrorMapper)); } /** - * Returns a Maybe that is based on applying a specified function to the item emitted by the source Single, - * where that function returns a MaybeSource. + * Returns a {@link Maybe} that is based on applying a specified function to the item emitted by the current {@code Single}, + * where that function returns a {@link MaybeSource}. *

* *

@@ -2727,23 +3468,24 @@ public final Single flatMap(Function the result value type * @param mapper - * a function that, when applied to the item emitted by the source Single, returns a MaybeSource - * @return the Maybe returned from {@code mapper} when applied to the item emitted by the source Single + * a function that, when applied to the item emitted by the current {@code Single}, returns a {@code MaybeSource} + * @return the new {@code Maybe} returned from {@code mapper} when applied to the item emitted by the current {@code Single} + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Maybe flatMapMaybe(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMapMaybe(this, mapper)); + public final <@NonNull R> Maybe flatMapMaybe(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapMaybe<>(this, mapper)); } /** - * Returns a Flowable that emits items based on applying a specified function to the item emitted by the - * source Single, where that function returns a Publisher. + * Returns a {@link Flowable} that emits items based on applying a specified function to the item emitted by the + * current {@code Single}, where that function returns a {@link Publisher}. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer @@ -2754,22 +3496,23 @@ public final Maybe flatMapMaybe(final Function the result value type * @param mapper - * a function that, when applied to the item emitted by the source Single, returns a - * Flowable - * @return the Flowable returned from {@code func} when applied to the item emitted by the source Single + * a function that, when applied to the item emitted by the current {@code Single}, returns a + * {@code Publisher} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flatMapPublisher(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMapPublisher(this, mapper)); + public final <@NonNull R> Flowable flatMapPublisher(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapPublisher<>(this, mapper)); } /** - * Maps the success value of the upstream {@link Single} into an {@link Iterable} and emits its items as a + * Maps the success value of the current {@code Single} into an {@link Iterable} and emits its items as a * {@link Flowable} sequence. *

* @@ -2781,24 +3524,26 @@ public final Flowable flatMapPublisher(Function * * @param - * the type of item emitted by the resulting Iterable + * the type of item emitted by the resulting {@code Iterable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Single - * @return the new Flowable instance + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Single} + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap + * @see #flattenStreamAsFlowable(Function) */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable flattenAsFlowable(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMapIterableFlowable(this, mapper)); + public final <@NonNull U> Flowable flattenAsFlowable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapIterableFlowable<>(this, mapper)); } /** - * Maps the success value of the upstream {@link Single} into an {@link Iterable} and emits its items as an + * Maps the success value of the current {@code Single} into an {@link Iterable} and emits its items as an * {@link Observable} sequence. *

* @@ -2808,26 +3553,28 @@ public final Flowable flattenAsFlowable(final Function * * @param - * the type of item emitted by the resulting Iterable + * the type of item emitted by the resulting {@code Iterable} * @param mapper - * a function that returns an Iterable sequence of values for when given an item emitted by the - * source Single - * @return the new Observable instance + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * current {@code Single} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap + * @see #flattenStreamAsObservable(Function) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flattenAsObservable(final Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMapIterableObservable(this, mapper)); + public final <@NonNull U> Observable flattenAsObservable(@NonNull Function<@NonNull ? super T, @NonNull ? extends Iterable> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapIterableObservable<>(this, mapper)); } /** - * Returns an Observable that is based on applying a specified function to the item emitted by the source Single, - * where that function returns an ObservableSource. + * Returns an {@link Observable} that is based on applying a specified function to the item emitted by the current {@code Single}, + * where that function returns an {@link ObservableSource}. *

- * + * *

*
Scheduler:
*
{@code flatMapObservable} does not operate by default on a particular {@link Scheduler}.
@@ -2835,21 +3582,22 @@ public final Observable flattenAsObservable(final Function the result value type * @param mapper - * a function that, when applied to the item emitted by the source Single, returns an ObservableSource - * @return the Observable returned from {@code func} when applied to the item emitted by the source Single + * a function that, when applied to the item emitted by the current {@code Single}, returns an {@code ObservableSource} + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Observable flatMapObservable(Function> mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMapObservable(this, mapper)); + public final <@NonNull R> Observable flatMapObservable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapObservable<>(this, mapper)); } /** * Returns a {@link Completable} that completes based on applying a specified function to the item emitted by the - * source {@link Single}, where that function returns a {@link Completable}. + * current {@code Single}, where that function returns a {@link CompletableSource}. *

* *

@@ -2858,22 +3606,23 @@ public final Observable flatMapObservable(Function * * @param mapper - * a function that, when applied to the item emitted by the source Single, returns a - * Completable - * @return the Completable returned from {@code func} when applied to the item emitted by the source Single + * a function that, when applied to the item emitted by the current {@code Single}, returns a + * {@code CompletableSource} + * @return the new {@code Completable} instance + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: FlatMap * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Completable flatMapCompletable(final Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleFlatMapCompletable(this, mapper)); + public final Completable flatMapCompletable(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlatMapCompletable<>(this, mapper)); } /** - * Waits in a blocking fashion until the current Single signals a success value (which is returned) or + * Waits in a blocking fashion until the current {@code Single} signals a success value (which is returned) or * an exception (which is propagated). *

* @@ -2889,12 +3638,113 @@ public final Completable flatMapCompletable(final Function observer = new BlockingMultiObserver(); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); subscribe(observer); return observer.blockingGet(); } + /** + * Subscribes to the current {@code Single} and blocks the current thread until it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If the current {@code Single} signals an error, + * the {@link Throwable} is routed to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, an {@link InterruptedException} is routed to the same global error handler. + *
+ *
+ * @since 3.0.0 + * @see #blockingSubscribe(Consumer) + * @see #blockingSubscribe(Consumer, Consumer) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe() { + blockingSubscribe(Functions.emptyConsumer(), Functions.ERROR_CONSUMER); + } + + /** + * Subscribes to the current {@code Single} and calls given {@code onSuccess} callback on the current thread + * when it completes normally. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either the current {@code Single} signals an error or {@code onSuccess} throws, + * the respective {@link Throwable} is routed to the global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, an {@link InterruptedException} is routed to the same global error handler. + *
+ *
+ * @param onSuccess the {@link Consumer} to call if the current {@code Single} succeeds + * @throws NullPointerException if {@code onSuccess} is {@code null} + * @since 3.0.0 + * @see #blockingSubscribe(Consumer, Consumer) + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Consumer onSuccess) { + blockingSubscribe(onSuccess, Functions.ERROR_CONSUMER); + } + + /** + * Subscribes to the current {@code Single} and calls the appropriate callback on the current thread + * when it terminates. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
If either {@code onSuccess} or {@code onError} throw, the {@link Throwable} is routed to the + * global error handler via {@link RxJavaPlugins#onError(Throwable)}. + * If the current thread is interrupted, the {@code onError} consumer is called with an {@link InterruptedException}. + *
+ *
+ * @param onSuccess the {@link Consumer} to call if the current {@code Single} succeeds + * @param onError the {@code Consumer} to call if the current {@code Single} signals an error + * @throws NullPointerException if {@code onSuccess} or {@code onError} is {@code null} + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull Consumer onSuccess, @NonNull Consumer onError) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + BlockingMultiObserver observer = new BlockingMultiObserver<>(); + subscribe(observer); + observer.blockingConsume(onSuccess, onError, Functions.EMPTY_ACTION); + } + + /** + * Subscribes to the current {@code Single} and calls the appropriate {@link SingleObserver} method on the current thread. + *

+ * + *

+ *
Scheduler:
+ *
{@code blockingSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
Error handling:
+ *
An {@code onError} signal is delivered to the {@link SingleObserver#onError(Throwable)} method. + * If any of the {@code SingleObserver}'s methods throw, the {@link RuntimeException} is propagated to the caller of this method. + * If the current thread is interrupted, an {@link InterruptedException} is delivered to {@code observer.onError}. + *
+ *
+ * @param observer the {@code SingleObserver} to call methods on the current thread + * @throws NullPointerException if {@code observer} is {@code null} + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void blockingSubscribe(@NonNull SingleObserver observer) { + Objects.requireNonNull(observer, "observer is null"); + BlockingDisposableMultiObserver blockingObserver = new BlockingDisposableMultiObserver<>(); + observer.onSubscribe(blockingObserver); + subscribe(blockingObserver); + blockingObserver.blockingConsume(observer); + } + /** * This method requires advanced knowledge about building operators, please consider * other standard composition methods first; @@ -3018,39 +3868,40 @@ public final T blockingGet() { * class and creating a {@link SingleTransformer} with it is recommended. *

* Note also that it is not possible to stop the subscription phase in {@code lift()} as the {@code apply()} method - * requires a non-null {@code SingleObserver} instance to be returned, which is then unconditionally subscribed to - * the upstream {@code Single}. For example, if the operator decided there is no reason to subscribe to the + * requires a non-{@code null} {@code SingleObserver} instance to be returned, which is then unconditionally subscribed to + * the current {@code Single}. For example, if the operator decided there is no reason to subscribe to the * upstream source because of some optimization possibility or a failure to prepare the operator, it still has to - * return a {@code SingleObserver} that should immediately dispose the upstream's {@code Disposable} in its + * return a {@code SingleObserver} that should immediately dispose the upstream's {@link Disposable} in its * {@code onSubscribe} method. Again, using a {@code SingleTransformer} and extending the {@code Single} is * a better option as {@link #subscribeActual} can decide to not subscribe to its upstream after all. *

*
Scheduler:
*
{@code lift} does not operate by default on a particular {@link Scheduler}, however, the - * {@link SingleOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
+ * {@code SingleOperator} may use a {@code Scheduler} to support its own asynchronous behavior.
*
* * @param the output value type - * @param lift the {@link SingleOperator} that receives the downstream's {@code SingleObserver} and should return + * @param lift the {@code SingleOperator} that receives the downstream's {@code SingleObserver} and should return * a {@code SingleObserver} with custom behavior to be used as the consumer for the current * {@code Single}. - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code lift} is {@code null} * @see RxJava wiki: Writing operators * @see #compose(SingleTransformer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single lift(final SingleOperator lift) { - ObjectHelper.requireNonNull(lift, "lift is null"); - return RxJavaPlugins.onAssembly(new SingleLift(this, lift)); + public final <@NonNull R> Single lift(@NonNull SingleOperator lift) { + Objects.requireNonNull(lift, "lift is null"); + return RxJavaPlugins.onAssembly(new SingleLift<>(this, lift)); } /** - * Returns a Single that applies a specified function to the item emitted by the source Single and + * Returns a {@code Single} that applies a specified function to the item emitted by the current {@code Single} and * emits the result of this function application. *

- * + * *

*
Scheduler:
*
{@code map} does not operate by default on a particular {@link Scheduler}.
@@ -3058,89 +3909,94 @@ public final Single lift(final SingleOperator lif * * @param the result value type * @param mapper - * a function to apply to the item emitted by the Single - * @return a Single that emits the item from the source Single, transformed by the specified function + * a function to apply to the item emitted by the {@code Single} + * @return the new {@code Single} that emits the item from the current {@code Single}, transformed by the specified function + * @throws NullPointerException if {@code mapper} is {@code null} * @see ReactiveX operators documentation: Map */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single map(Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); - return RxJavaPlugins.onAssembly(new SingleMap(this, mapper)); + public final <@NonNull R> Single map(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleMap<>(this, mapper)); } /** - * Maps the signal types of this Single into a {@link Notification} of the same kind + * Maps the signal types of this {@code Single} into a {@link Notification} of the same kind * and emits it as a single success value to downstream. *

- * + * *

*
Scheduler:
*
{@code materialize} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.2.4 - experimental - * @return the new Single instance + * @return the new {@code Single} instance * @since 3.0.0 * @see #dematerialize(Function) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single> materialize() { - return RxJavaPlugins.onAssembly(new SingleMaterialize(this)); + return RxJavaPlugins.onAssembly(new SingleMaterialize<>(this)); } /** - * Signals true if the current Single signals a success value that is Object-equals with the value + * Signals {@code true} if the current {@code Single} signals a success value that is {@link Object#equals(Object)} with the value * provided. *

- * + * *

* *

*
Scheduler:
*
{@code contains} does not operate by default on a particular {@link Scheduler}.
*
- * @param value the value to compare against the success value of this Single - * @return the new Single instance + * @param item the value to compare against the success value of this {@code Single} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code item} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single contains(Object value) { - return contains(value, ObjectHelper.equalsPredicate()); + @NonNull + public final Single contains(@NonNull Object item) { + return contains(item, ObjectHelper.equalsPredicate()); } /** - * Signals true if the current Single signals a success value that is equal with - * the value provided by calling a bi-predicate. + * Signals {@code true} if the current {@code Single} signals a success value that is equal with + * the value provided by calling a {@link BiPredicate}. *

* *

*
Scheduler:
*
{@code contains} does not operate by default on a particular {@link Scheduler}.
*
- * @param value the value to compare against the success value of this Single - * @param comparer the function that receives the success value of this Single, the value provided - * and should return true if they are considered equal - * @return the new Single instance + * @param item the value to compare against the success value of this {@code Single} + * @param comparer the function that receives the success value of this {@code Single}, the value provided + * and should return {@code true} if they are considered equal + * @return the new {@code Single} instance + * @throws NullPointerException if {@code item} or {@code comparer} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single contains(final Object value, final BiPredicate comparer) { - ObjectHelper.requireNonNull(value, "value is null"); - ObjectHelper.requireNonNull(comparer, "comparer is null"); - return RxJavaPlugins.onAssembly(new SingleContains(this, value, comparer)); + public final Single contains(@NonNull Object item, @NonNull BiPredicate comparer) { + Objects.requireNonNull(item, "item is null"); + Objects.requireNonNull(comparer, "comparer is null"); + return RxJavaPlugins.onAssembly(new SingleContains<>(this, item, comparer)); } /** - * Flattens this and another Single into a single Flowable, without any transformation. + * Flattens this {@code Single} and another {@link SingleSource} into one {@link Flowable}, without any transformation. *

- * + * *

- * You can combine items emitted by multiple Singles so that they appear as a single Flowable, by using + * You can combine items emitted by multiple {@code SingleSource}s so that they appear as one {@code Flowable}, by using * the {@code mergeWith} method. *

*
Backpressure:
@@ -3150,32 +4006,58 @@ public final Single contains(final Object value, final BiPredicate * * @param other - * a SingleSource to be merged - * @return that emits all of the items emitted by the source Singles + * a {@code SingleSource} to be merged + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: Merge */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable mergeWith(SingleSource other) { + @NonNull + public final Flowable mergeWith(@NonNull SingleSource other) { return merge(this, other); } - /** - * Modifies a Single to emit its item (or notify of its error) on a specified {@link Scheduler}, - * asynchronously. + * Filters the items emitted by the current {@code Single}, only emitting its success value if that + * is an instance of the supplied {@link Class}. *

- * + * *

- *
Scheduler:
- *
you specify which {@link Scheduler} this operator will use.
+ *
Scheduler:
+ *
{@code ofType} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the output type + * @param clazz + * the class type to filter the items emitted by the current {@code Single} + * @return the new {@link Maybe} instance + * @throws NullPointerException if {@code clazz} is {@code null} + * @see ReactiveX operators documentation: Filter + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull U> Maybe ofType(@NonNull Class clazz) { + Objects.requireNonNull(clazz, "clazz is null"); + return filter(Functions.isInstanceOf(clazz)).cast(clazz); + } + + /** + * Signals the success item or the terminal signals of the current {@code Single} on the specified {@link Scheduler}, + * asynchronously. + *

+ * + *

+ *
Scheduler:
+ *
you specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to notify subscribers on - * @return the source Single modified so that its subscribers are notified on the specified - * {@link Scheduler} - * @throws NullPointerException if scheduler is null + * the {@code Scheduler} to notify subscribers on + * @return the new {@code Single} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: ObserveOn * @see RxJava Threading Examples * @see #subscribeOn @@ -3183,22 +4065,22 @@ public final Flowable mergeWith(SingleSource other) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single observeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new SingleObserveOn(this, scheduler)); + public final Single observeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleObserveOn<>(this, scheduler)); } /** - * Instructs a Single to emit an item (returned by a specified function) rather than invoking - * {@link SingleObserver#onError onError} if it encounters an error. + * Ends the flow with a success item returned by a function for the {@link Throwable} error signaled by the current + * {@code Single} instead of signaling the error via {@code onError}. *

- * + * *

- * By default, when a Single encounters an error that prevents it from emitting the expected item to its - * subscriber, the Single invokes its subscriber's {@link SingleObserver#onError} method, and then quits - * without invoking any more of its subscriber's methods. The {@code onErrorReturn} method changes this - * behavior. If you pass a function ({@code resumeFunction}) to a Single's {@code onErrorReturn} method, if - * the original Single encounters an error, instead of invoking its subscriber's + * By default, when a {@code Single} encounters an error that prevents it from emitting the expected item to its + * subscriber, the {@code Single} invokes its subscriber's {@link SingleObserver#onError} method, and then quits + * without invoking any more of its observer's methods. The {@code onErrorReturn} method changes this + * behavior. If you pass a function ({@code resumeFunction}) to a {@code Single}'s {@code onErrorReturn} method, if + * the original {@code Single} encounters an error, instead of invoking its observer's * {@link SingleObserver#onError} method, it will instead emit the return value of {@code resumeFunction}. *

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -3208,54 +4090,56 @@ public final Single observeOn(final Scheduler scheduler) { *

{@code onErrorReturn} does not operate by default on a particular {@link Scheduler}.
*
* - * @param resumeFunction - * a function that returns an item that the new Single will emit if the source Single encounters + * @param itemSupplier + * a function that returns an item that the new {@code Single} will emit if the current {@code Single} encounters * an error - * @return the original Single with appropriately modified behavior + * @return the new {@code Single} instance + * @throws NullPointerException if {@code itemSupplier} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single onErrorReturn(final Function resumeFunction) { - ObjectHelper.requireNonNull(resumeFunction, "resumeFunction is null"); - return RxJavaPlugins.onAssembly(new SingleOnErrorReturn(this, resumeFunction, null)); + public final Single onErrorReturn(@NonNull Function itemSupplier) { + Objects.requireNonNull(itemSupplier, "itemSupplier is null"); + return RxJavaPlugins.onAssembly(new SingleOnErrorReturn<>(this, itemSupplier, null)); } /** - * Signals the specified value as success in case the current Single signals an error. + * Signals the specified value as success in case the current {@code Single} signals an error. *

- * + * *

*
Scheduler:
*
{@code onErrorReturnItem} does not operate by default on a particular {@link Scheduler}.
*
- * @param value the value to signal if the current Single fails - * @return the new Single instance + * @param item the value to signal if the current {@code Single} fails + * @return the new {@code Single} instance + * @throws NullPointerException if {@code item} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single onErrorReturnItem(final T value) { - ObjectHelper.requireNonNull(value, "value is null"); - return RxJavaPlugins.onAssembly(new SingleOnErrorReturn(this, null, value)); + public final Single onErrorReturnItem(@NonNull T item) { + Objects.requireNonNull(item, "item is null"); + return RxJavaPlugins.onAssembly(new SingleOnErrorReturn<>(this, null, item)); } /** - * Instructs a Single to pass control to another Single rather than invoking - * {@link SingleObserver#onError(Throwable)} if it encounters an error. + * Resumes the flow with the given {@link SingleSource} when the current {@code Single} fails instead of + * signaling the error via {@code onError}. *

- * + * *

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

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -3265,32 +4149,76 @@ public final Single onErrorReturnItem(final T value) { *

{@code onErrorResumeWith} does not operate by default on a particular {@link Scheduler}.
*
* - * @param resumeSingleInCaseOfError a Single that will take control if source Single encounters an error. - * @return the original Single, with appropriately modified behavior. + * @param fallback a {@code Single} that will take control if source {@code Single} encounters an error. + * @return the new {@code Single} instance + * @throws NullPointerException if {@code fallback} is {@code null} * @see ReactiveX operators documentation: Catch */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single onErrorResumeWith(final SingleSource resumeSingleInCaseOfError) { - ObjectHelper.requireNonNull(resumeSingleInCaseOfError, "resumeSingleInCaseOfError is null"); - return onErrorResumeNext(Functions.justFunction(resumeSingleInCaseOfError)); + public final Single onErrorResumeWith(@NonNull SingleSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return onErrorResumeNext(Functions.justFunction(fallback)); + } + + /** + * Returns a {@link Maybe} instance that if the current {@code Single} emits an error, it will emit an {@code onComplete} + * and swallow the throwable. + *

+ * + *

+ *
Scheduler:
+ *
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code Maybe} instance + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Maybe onErrorComplete() { + return onErrorComplete(Functions.alwaysTrue()); + } + + /** + * Returns a {@link Maybe} instance that if this {@code Single} emits an error and the predicate returns + * {@code true}, it will emit an {@code onComplete} and swallow the throwable. + *

+ * + *

+ *
Scheduler:
+ *
{@code onErrorComplete} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param predicate the predicate to call when an {@link Throwable} is emitted which should return {@code true} + * if the {@code Throwable} should be swallowed and replaced with an {@code onComplete}. + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code predicate} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Maybe onErrorComplete(@NonNull Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + + return RxJavaPlugins.onAssembly(new SingleOnErrorComplete<>(this, predicate)); } /** - * Instructs a Single to pass control to another Single rather than invoking - * {@link SingleObserver#onError(Throwable)} if it encounters an error. + * Resumes the flow with a {@link SingleSource} returned for the failure {@link Throwable} of the current {@code Single} by a + * function instead of signaling the error via {@code onError}. *

- * + * *

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

* You can use this to prevent errors from propagating or to supply fallback data should errors be @@ -3300,8 +4228,9 @@ public final Single onErrorResumeWith(final SingleSource resumeS *

{@code onErrorResumeNext} does not operate by default on a particular {@link Scheduler}.
*
* - * @param resumeFunctionInCaseOfError a function that returns a Single that will take control if source Single encounters an error. - * @return the original Single, with appropriately modified behavior. + * @param fallbackSupplier a function that returns a {@code SingleSource} that will take control if source {@code Single} encounters an error. + * @return the new {@code Single} instance + * @throws NullPointerException if {@code fallbackSupplier} is {@code null} * @see ReactiveX operators documentation: Catch * @since .20 */ @@ -3309,14 +4238,14 @@ public final Single onErrorResumeWith(final SingleSource resumeS @NonNull @SchedulerSupport(SchedulerSupport.NONE) public final Single onErrorResumeNext( - final Function> resumeFunctionInCaseOfError) { - ObjectHelper.requireNonNull(resumeFunctionInCaseOfError, "resumeFunctionInCaseOfError is null"); - return RxJavaPlugins.onAssembly(new SingleResumeNext(this, resumeFunctionInCaseOfError)); + @NonNull Function> fallbackSupplier) { + Objects.requireNonNull(fallbackSupplier, "fallbackSupplier is null"); + return RxJavaPlugins.onAssembly(new SingleResumeNext<>(this, fallbackSupplier)); } /** - * Nulls out references to the upstream producer and downstream SingleObserver if - * the sequence is terminated or downstream calls dispose(). + * Nulls out references to the upstream producer and downstream {@link SingleObserver} if + * the sequence is terminated or downstream calls {@code dispose()}. *

* *

@@ -3324,63 +4253,67 @@ public final Single onErrorResumeNext( *
{@code onTerminateDetach} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.5 - experimental - * @return a Single which nulls out references to the upstream producer and downstream SingleObserver if - * the sequence is terminated or downstream calls dispose() + * @return the new {@code Single} which {@code null}s out references to the upstream producer and downstream {@code SingleObserver} if + * the sequence is terminated or downstream calls {@code dispose()} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single onTerminateDetach() { - return RxJavaPlugins.onAssembly(new SingleDetach(this)); + return RxJavaPlugins.onAssembly(new SingleDetach<>(this)); } /** - * Repeatedly re-subscribes to the current Single and emits each success value. + * Repeatedly re-subscribes to the current {@code Single} and emits each success value as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Flowable instance + * @return the new {@code Flowable} instance * @since 2.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable repeat() { return toFlowable().repeat(); } /** - * Re-subscribes to the current Single at most the given number of times and emits each success value. + * Re-subscribes to the current {@code Single} at most the given number of times and emits each success value as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code repeat} does not operate by default on a particular {@link Scheduler}.
*
- * @param times the number of times to re-subscribe to the current Single - * @return the new Flowable instance + * @param times the number of times to re-subscribe to the current {@code Single} + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code times} is negative * @since 2.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Flowable repeat(long times) { return toFlowable().repeat(times); } /** - * Re-subscribes to the current Single if - * the Publisher returned by the handler function signals a value in response to a - * value signalled through the Flowable the handle receives. + * Re-subscribes to the current {@code Single} if + * the {@link Publisher} returned by the handler function signals a value in response to a + * value signaled through the {@link Flowable} the handler receives. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer. @@ -3388,101 +4321,111 @@ public final Flowable repeat(long times) { *
Scheduler:
*
{@code repeatWhen} does not operate by default on a particular {@link Scheduler}.
*
- * @param handler the function that is called with a Flowable that signals a value when the Single - * signalled a success value and returns a Publisher that has to signal a value to - * trigger a resubscription to the current Single, otherwise the terminal signal of - * the Publisher will be the terminal signal of the sequence as well. - * @return the new Flowable instance + * @param handler the function that is called with a {@code Flowable} that signals a value when the {@code Single} + * signaled a success value and returns a {@code Publisher} that has to signal a value to + * trigger a resubscription to the current {@code Single}, otherwise the terminal signal of + * the {@code Publisher} will be the terminal signal of the sequence as well. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code handler} is {@code null} * @since 2.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable repeatWhen(Function, ? extends Publisher> handler) { + @NonNull + public final Flowable repeatWhen(@NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { return toFlowable().repeatWhen(handler); } /** - * Re-subscribes to the current Single until the given BooleanSupplier returns true. + * Re-subscribes to the current {@code Single} until the given {@link BooleanSupplier} returns {@code true} + * and emits the success items as a {@link Flowable} sequence. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
*
Scheduler:
*
{@code repeatUntil} does not operate by default on a particular {@link Scheduler}.
*
- * @param stop the BooleanSupplier called after the current Single succeeds and if returns false, - * the Single is re-subscribed; otherwise the sequence completes. - * @return the new Flowable instance + * @param stop the {@code BooleanSupplier} called after the current {@code Single} succeeds and if returns {@code false}, + * the {@code Single} is re-subscribed; otherwise the sequence completes. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code stop} is {@code null} * @since 2.0 */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Flowable repeatUntil(BooleanSupplier stop) { + @NonNull + public final Flowable repeatUntil(@NonNull BooleanSupplier stop) { return toFlowable().repeatUntil(stop); } /** - * Repeatedly re-subscribes to the current Single indefinitely if it fails with an onError. + * Repeatedly re-subscribes to the current {@code Single} indefinitely if it fails with an {@code onError}. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Single instance + * @return the new {@code Single} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single retry() { return toSingle(toFlowable().retry()); } /** - * Repeatedly re-subscribe at most the specified times to the current Single - * if it fails with an onError. + * Repeatedly re-subscribe at most the specified times to the current {@code Single} + * if it fails with an {@code onError}. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param times the number of times to resubscribe if the current Single fails - * @return the new Single instance + * @param times the number of times to resubscribe if the current {@code Single} fails + * @return the new {@code Single} instance + * @throws IllegalArgumentException if {@code times} is negative * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Single retry(long times) { return toSingle(toFlowable().retry(times)); } /** - * Re-subscribe to the current Single if the given predicate returns true when the Single fails - * with an onError. + * Re-subscribe to the current {@code Single} if the given predicate returns {@code true} when the {@code Single} fails + * with an {@code onError}. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param predicate the predicate called with the resubscription count and the failure Throwable - * and should return true if a resubscription should happen - * @return the new Single instance + * @param predicate the predicate called with the resubscription count and the failure {@link Throwable} + * and should return {@code true} if a resubscription should happen + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single retry(BiPredicate predicate) { + @NonNull + public final Single retry(@NonNull BiPredicate predicate) { return toSingle(toFlowable().retry(predicate)); } /** - * Repeatedly re-subscribe at most times or until the predicate returns false, whichever happens first - * if it fails with an onError. + * Repeatedly re-subscribe at most times or until the predicate returns {@code false}, whichever happens first + * if it fails with an {@code onError}. *

* *

@@ -3490,50 +4433,76 @@ public final Single retry(BiPredicate pre *
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.1.8 - experimental - * @param times the number of times to resubscribe if the current Single fails - * @param predicate the predicate called with the failure Throwable - * and should return true if a resubscription should happen - * @return the new Single instance + * @param times the number of times to resubscribe if the current {@code Single} fails + * @param predicate the predicate called with the failure {@link Throwable} + * and should return {@code true} if a resubscription should happen + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} + * @throws IllegalArgumentException if {@code times} is negative * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single retry(long times, Predicate predicate) { + @NonNull + public final Single retry(long times, @NonNull Predicate predicate) { return toSingle(toFlowable().retry(times, predicate)); } /** - * Re-subscribe to the current Single if the given predicate returns true when the Single fails - * with an onError. + * Re-subscribe to the current {@code Single} if the given predicate returns {@code true} when the {@code Single} fails + * with an {@code onError}. *

* *

*
Scheduler:
*
{@code retry} does not operate by default on a particular {@link Scheduler}.
*
- * @param predicate the predicate called with the failure Throwable - * and should return true if a resubscription should happen - * @return the new Single instance + * @param predicate the predicate called with the failure {@link Throwable} + * and should return {@code true} if a resubscription should happen + * @return the new {@code Single} instance + * @throws NullPointerException if {@code predicate} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single retry(Predicate predicate) { + @NonNull + public final Single retry(@NonNull Predicate predicate) { return toSingle(toFlowable().retry(predicate)); } /** - * Re-subscribes to the current Single if and when the Publisher returned by the handler + * Retries until the given stop function returns {@code true}. + *

+ * + *

+ *
Scheduler:
+ *
{@code retryUntil} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param stop the function that should return {@code true} to stop retrying + * @return the new {@code Single} instance + * @throws NullPointerException if {@code stop} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Single retryUntil(@NonNull BooleanSupplier stop) { + Objects.requireNonNull(stop, "stop is null"); + return retry(Long.MAX_VALUE, Functions.predicateReverseFor(stop)); + } + + /** + * Re-subscribes to the current {@code Single} if and when the {@link Publisher} returned by the handler * function signals a value. *

* *

- * If the Publisher signals an {@code onComplete}, the resulting {@code Single} will signal a {@link NoSuchElementException}. + * If the {@code Publisher} signals an {@code onComplete}, the resulting {@code Single} will signal a {@link NoSuchElementException}. *

* Note that the inner {@code Publisher} returned by the handler function should signal * either {@code onNext}, {@code onError} or {@code onComplete} in response to the received - * {@code Throwable} to indicate the operator should retry or terminate. If the upstream to - * the operator is asynchronous, signaling onNext followed by onComplete immediately may + * {@link Throwable} to indicate the operator should retry or terminate. If the upstream to + * the operator is asynchronous, signaling {@code onNext} followed by {@code onComplete} immediately may * result in the sequence to be completed immediately. Similarly, if this inner * {@code Publisher} signals {@code onError} or {@code onComplete} while the upstream is * active, the sequence is terminated with the same signal immediately. @@ -3559,41 +4528,193 @@ public final Single retry(Predicate predicate) { *

{@code retryWhen} does not operate by default on a particular {@link Scheduler}.
*
* - * @param handler the function that receives a Flowable of the error the Single emits and should - * return a Publisher that should signal a normal value (in response to the - * throwable the Flowable emits) to trigger a resubscription or signal an error to - * be the output of the resulting Single - * @return the new Single instance + * @param handler the function that receives a {@link Flowable} of the error the {@code Single} emits and should + * return a {@code Publisher} that should signal a normal value (in response to the + * throwable the {@code Flowable} emits) to trigger a resubscription or signal an error to + * be the output of the resulting {@code Single} + * @return the new {@code Single} instance + * @throws NullPointerException if {@code handler} is {@code null} */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single retryWhen(Function, ? extends Publisher> handler) { + @NonNull + public final Single retryWhen(@NonNull Function, @NonNull ? extends Publisher<@NonNull ?>> handler) { return toSingle(toFlowable().retryWhen(handler)); } /** - * Subscribes to a Single but ignore its emission or notification. + * Wraps the given {@link SingleObserver}, catches any {@link RuntimeException}s thrown by its + * {@link SingleObserver#onSubscribe(Disposable)}, {@link SingleObserver#onSuccess(Object)} or + * {@link SingleObserver#onError(Throwable)} methods* and routes those to the global error handler + * via {@link RxJavaPlugins#onError(Throwable)}. + *

+ * By default, the {@code Single} protocol forbids the {@code onXXX} methods to throw, but some + * {@code SingleObserver} implementation may do it anyway, causing undefined behavior in the + * upstream. This method and the underlying safe wrapper ensures such misbehaving consumers don't + * disrupt the protocol. + *

+ *
Scheduler:
+ *
{@code safeSubscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param observer the potentially misbehaving {@code SingleObserver} + * @throws NullPointerException if {@code observer} is {@code null} + * @see #subscribe(Consumer,Consumer) + * @since 3.0.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + public final void safeSubscribe(@NonNull SingleObserver observer) { + Objects.requireNonNull(observer, "observer is null"); + subscribe(new SafeSingleObserver<>(observer)); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link CompletableSource} + * then the current {@code Single} if the other completed normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code CompletableSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Completable.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link SingleSource} + * then the current {@code Single} if the other succeeded normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code SingleSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Single.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns a {@link Flowable} which first runs the other {@link MaybeSource} + * then the current {@code Single} if the other succeeded or completed normally. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code MaybeSource} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public final Flowable startWith(@NonNull MaybeSource other) { + Objects.requireNonNull(other, "other is null"); + return Flowable.concat(Maybe.wrap(other).toFlowable(), toFlowable()); + } + + /** + * Returns an {@link Observable} which first delivers the events + * of the other {@link ObservableSource} then runs the current {@code Single}. + *

+ * + *

+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code ObservableSource} to run first + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + public final Observable startWith(@NonNull ObservableSource other) { + Objects.requireNonNull(other, "other is null"); + return Observable.wrap(other).concatWith(this.toObservable()); + } + + /** + * Returns a {@link Flowable} which first delivers the events + * of the other {@link Publisher} then runs the current {@code Single}. + *

+ * + *

+ *
Backpressure:
+ *
The returned {@code Flowable} honors the backpressure of the downstream consumer + * and expects the other {@code Publisher} to honor it as well.
+ *
Scheduler:
+ *
{@code startWith} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param other the other {@code Publisher} to run first + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code other} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final Flowable startWith(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return toFlowable().startWith(other); + } + + /** + * Subscribes to a {@code Single} but ignore its emission or notification. *

* *

- * If the Single emits an error, it is wrapped into an + * If the {@code Single} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Disposable} reference can request the {@link Single} stop work. + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, DisposableContainer) */ @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Disposable subscribe() { return subscribe(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING); } /** - * Subscribes to a Single and provides a composite callback to handle the item it emits + * Subscribes to a {@code Single} and provides a composite callback to handle the item it emits * or any error notification it issues. *

* @@ -3603,52 +4724,55 @@ public final Disposable subscribe() { *

* * @param onCallback - * the callback that receives either the success value or the failure Throwable - * (whichever is not null) - * @return a {@link Disposable} reference can request the {@link Single} stop work. - * @see ReactiveX operators documentation: Subscribe + * the callback that receives either the success value or the failure {@link Throwable} + * (whichever is not {@code null}) + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @throws NullPointerException - * if {@code onCallback} is null + * if {@code onCallback} is {@code null} + * @see #subscribe(Consumer, Consumer, DisposableContainer) + * @see ReactiveX operators documentation: Subscribe */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(final BiConsumer onCallback) { - ObjectHelper.requireNonNull(onCallback, "onCallback is null"); + public final Disposable subscribe(@NonNull BiConsumer<@Nullable ? super T, @Nullable ? super Throwable> onCallback) { + Objects.requireNonNull(onCallback, "onCallback is null"); - BiConsumerSingleObserver observer = new BiConsumerSingleObserver(onCallback); + BiConsumerSingleObserver observer = new BiConsumerSingleObserver<>(onCallback); subscribe(observer); return observer; } /** - * Subscribes to a Single and provides a callback to handle the item it emits. + * Subscribes to a {@code Single} and provides a callback to handle the item it emits. *

* *

- * If the Single emits an error, it is wrapped into an + * If the {@code Single} emits an error, it is wrapped into an * {@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException} - * and routed to the RxJavaPlugins.onError handler. + * and routed to the {@link RxJavaPlugins#onError(Throwable)} handler. *

*
Scheduler:
*
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
*
* * @param onSuccess - * the {@code Consumer} you have designed to accept the emission from the Single - * @return a {@link Disposable} reference can request the {@link Single} stop work. + * the {@code Consumer} you have designed to accept the emission from the {@code Single} + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @throws NullPointerException - * if {@code onSuccess} is null + * if {@code onSuccess} is {@code null} * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, DisposableContainer) */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(Consumer onSuccess) { + @NonNull + public final Disposable subscribe(@NonNull Consumer onSuccess) { return subscribe(onSuccess, Functions.ON_ERROR_MISSING); } /** - * Subscribes to a Single and provides callbacks to handle the item it emits or any error notification it + * Subscribes to a {@code Single} and provides callbacks to handle the item it emits or any error notification it * issues. *

* @@ -3658,36 +4782,74 @@ public final Disposable subscribe(Consumer onSuccess) { *

* * @param onSuccess - * the {@code Consumer} you have designed to accept the emission from the Single + * the {@code Consumer} you have designed to accept the emission from the {@code Single} * @param onError * the {@code Consumer} you have designed to accept any error notification from the - * Single - * @return a {@link Disposable} reference can request the {@link Single} stop work. - * @see ReactiveX operators documentation: Subscribe + * {@code Single} + * @return the new {@link Disposable} instance that can be used for disposing the subscription at any time * @throws NullPointerException - * if {@code onSuccess} is null, or - * if {@code onError} is null + * if {@code onSuccess} or {@code onError} is {@code null} + * @see ReactiveX operators documentation: Subscribe + * @see #subscribe(Consumer, Consumer, DisposableContainer) */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Disposable subscribe(final Consumer onSuccess, final Consumer onError) { - ObjectHelper.requireNonNull(onSuccess, "onSuccess is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); + public final Disposable subscribe(@NonNull Consumer onSuccess, @NonNull Consumer onError) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); - ConsumerSingleObserver observer = new ConsumerSingleObserver(onSuccess, onError); + ConsumerSingleObserver observer = new ConsumerSingleObserver<>(onSuccess, onError); + subscribe(observer); + return observer; + } + + /** + * Wraps the given onXXX callbacks into a {@link Disposable} {@link SingleObserver}, + * adds it to the given {@link DisposableContainer} and ensures, that if the upstream + * terminates or this particular {@code Disposable} is disposed, the {@code SingleObserver} is removed + * from the given container. + *

+ * The {@code SingleObserver} will be removed after the callback for the terminal event has been invoked. + *

+ *
Scheduler:
+ *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param onSuccess the callback for upstream items + * @param onError the callback for an upstream error if any + * @param container the {@code DisposableContainer} (such as {@link CompositeDisposable}) to add and remove the + * created {@code Disposable} {@code SingleObserver} + * @return the {@code Disposable} that allows disposing the particular subscription. + * @throws NullPointerException + * if {@code onSuccess}, {@code onError} + * or {@code container} is {@code null} + * @since 3.1.0 + */ + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final Disposable subscribe( + @NonNull Consumer onSuccess, + @NonNull Consumer onError, + @NonNull DisposableContainer container) { + Objects.requireNonNull(onSuccess, "onSuccess is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(container, "container is null"); + + DisposableAutoReleaseMultiObserver observer = new DisposableAutoReleaseMultiObserver<>( + container, onSuccess, onError, Functions.EMPTY_ACTION); + container.add(observer); subscribe(observer); return observer; } @SchedulerSupport(SchedulerSupport.NONE) @Override - public final void subscribe(SingleObserver observer) { - ObjectHelper.requireNonNull(observer, "observer is null"); + public final void subscribe(@NonNull SingleObserver observer) { + Objects.requireNonNull(observer, "observer is null"); observer = RxJavaPlugins.onSubscribe(this, observer); - ObjectHelper.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null SingleObserver. Please check the handler provided to RxJavaPlugins.setOnSingleSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); + Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null SingleObserver. Please check the handler provided to RxJavaPlugins.setOnSingleSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins"); try { subscribeActual(observer); @@ -3706,13 +4868,13 @@ public final void subscribe(SingleObserver observer) { *

There is no need to call any of the plugin hooks on the current {@code Single} instance or * the {@code SingleObserver}; all hooks and basic safeguards have been * applied by {@link #subscribe(SingleObserver)} before this method gets called. - * @param observer the SingleObserver to handle, not null + * @param observer the {@code SingleObserver} to handle, not {@code null} */ protected abstract void subscribeActual(@NonNull SingleObserver observer); /** - * Subscribes a given SingleObserver (subclass) to this Single and returns the given - * SingleObserver as is. + * Subscribes a given {@link SingleObserver} (subclass) to this {@code Single} and returns the given + * {@code SingleObserver} as is. *

* *

Usage example: @@ -3730,31 +4892,33 @@ public final void subscribe(SingleObserver observer) { *

Scheduler:
*
{@code subscribeWith} does not operate by default on a particular {@link Scheduler}.
*
- * @param the type of the SingleObserver to use and return - * @param observer the SingleObserver (subclass) to use and return, not null + * @param the type of the {@code SingleObserver} to use and return + * @param observer the {@code SingleObserver} (subclass) to use and return, not {@code null} * @return the input {@code observer} - * @throws NullPointerException if {@code observer} is null + * @throws NullPointerException if {@code observer} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final > E subscribeWith(E observer) { + @NonNull + public final <@NonNull E extends SingleObserver> E subscribeWith(E observer) { subscribe(observer); return observer; } /** - * Asynchronously subscribes subscribers to this Single on the specified {@link Scheduler}. + * Asynchronously subscribes {@link SingleObserver}s to this {@code Single} on the specified {@link Scheduler}. *

- * + * *

*
Scheduler:
- *
You specify which {@link Scheduler} this operator will use.
+ *
You specify which {@code Scheduler} this operator will use.
*
* * @param scheduler - * the {@link Scheduler} to perform subscription actions on - * @return the source Single modified so that its subscriptions happen on the specified {@link Scheduler} + * the {@code Scheduler} to perform subscription actions on + * @return the new {@code Single} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @see ReactiveX operators documentation: SubscribeOn * @see RxJava Threading Examples * @see #observeOn @@ -3762,13 +4926,239 @@ public final > E subscribeWith(E observer) { @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single subscribeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new SingleSubscribeOn(this, scheduler)); + public final Single subscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleSubscribeOn<>(this, scheduler)); + } + + /** + * Measures the time (in milliseconds) between the subscription and success item emission + * of the current {@code Single} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Single} fails, the resulting {@code Single} will + * pass along the signal to the downstream. To measure the time to error, + * use {@link #materialize()} and apply {@link #timeInterval()}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the {@code computation} {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Single}.
+ *
+ * @return the new {@code Single} instance + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Single> timeInterval() { + return timeInterval(TimeUnit.MILLISECONDS, Schedulers.computation()); } /** - * Returns a Single that emits the item emitted by the source Single until a Completable terminates. Upon + * Measures the time (in milliseconds) between the subscription and success item emission + * of the current {@code Single} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Single} fails, the resulting {@code Single} will + * pass along the signal to the downstream. To measure the time to error, + * use {@link #materialize()} and apply {@link #timeInterval(Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the provided {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Single}.
+ *
+ * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Single} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Single> timeInterval(@NonNull Scheduler scheduler) { + return timeInterval(TimeUnit.MILLISECONDS, scheduler); + } + + /** + * Measures the time between the subscription and success item emission + * of the current {@code Single} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Single} fails, the resulting {@code Single} will + * pass along the signals to the downstream. To measure the time to error, + * use {@link #materialize()} and apply {@link #timeInterval(TimeUnit, Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the {@code computation} {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Single}.
+ *
+ * @param unit the time unit for measurement + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Single> timeInterval(@NonNull TimeUnit unit) { + return timeInterval(unit, Schedulers.computation()); + } + + /** + * Measures the time between the subscription and success item emission + * of the current {@code Single} and signals it as a tuple ({@link Timed}) + * success value. + *

+ * + *

+ * If the current {@code Single} is empty or fails, the resulting {@code Single} will + * pass along the signals to the downstream. To measure the time to termination, + * use {@link #materialize()} and apply {@link #timeInterval(TimeUnit, Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timeInterval} uses the provided {@link Scheduler} + * for determining the current time upon subscription and upon receiving the + * success item from the current {@code Single}.
+ *
+ * @param unit the time unit for measurement + * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Single> timeInterval(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleTimeInterval<>(this, unit, scheduler, true)); + } + + /** + * Combines the success value from the current {@code Single} with the current time (in milliseconds) of + * its reception, using the {@code computation} {@link Scheduler} as time source, + * then signals them as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Single} is empty or fails, the resulting {@code Single} will + * pass along the signals to the downstream. To get the timestamp of the error, + * use {@link #materialize()} and apply {@link #timestamp()}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the {@code computation} {@code Scheduler} + * for determining the current time upon receiving the + * success item from the current {@code Single}.
+ *
+ * @return the new {@code Single} instance + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Single> timestamp() { + return timestamp(TimeUnit.MILLISECONDS, Schedulers.computation()); + } + + /** + * Combines the success value from the current {@code Single} with the current time (in milliseconds) of + * its reception, using the given {@link Scheduler} as time source, + * then signals them as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Single} is empty or fails, the resulting {@code Single} will + * pass along the signals to the downstream. To get the timestamp of the error, + * use {@link #materialize()} and apply {@link #timestamp(Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the provided {@code Scheduler} + * for determining the current time upon receiving the + * success item from the current {@code Single}.
+ *
+ * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Single} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Single> timestamp(@NonNull Scheduler scheduler) { + return timestamp(TimeUnit.MILLISECONDS, scheduler); + } + + /** + * Combines the success value from the current {@code Single} with the current time of + * its reception, using the {@code computation} {@link Scheduler} as time source, + * then signals it as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Single} is empty or fails, the resulting {@code Single} will + * pass along the signals to the downstream. To get the timestamp of the error, + * use {@link #materialize()} and apply {@link #timestamp(TimeUnit)}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the {@code computation} {@code Scheduler}, + * for determining the current time upon receiving the + * success item from the current {@code Single}.
+ *
+ * @param unit the time unit for measurement + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.COMPUTATION) + public final Single> timestamp(@NonNull TimeUnit unit) { + return timestamp(unit, Schedulers.computation()); + } + + /** + * Combines the success value from the current {@code Single} with the current time of + * its reception, using the given {@link Scheduler} as time source, + * then signals it as a {@link Timed} instance. + *

+ * + *

+ * If the current {@code Single} is empty or fails, the resulting {@code Single} will + * pass along the signals to the downstream. To get the timestamp of the error, + * use {@link #materialize()} and apply {@link #timestamp(TimeUnit, Scheduler)}. + *

+ *
Scheduler:
+ *
{@code timestamp} uses the provided {@code Scheduler}, + * which is used for determining the current time upon receiving the + * success item from the current {@code Single}.
+ *
+ * @param unit the time unit for measurement + * @param scheduler the {@code Scheduler} used for providing the current time + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.CUSTOM) + public final Single> timestamp(@NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleTimeInterval<>(this, unit, scheduler, false)); + } + + /** + * Returns a {@code Single} that emits the item emitted by the current {@code Single} until a {@link CompletableSource} terminates. Upon * termination of {@code other}, this will emit a {@link CancellationException} rather than go to * {@link SingleObserver#onSuccess(Object)}. *

@@ -3779,21 +5169,22 @@ public final Single subscribeOn(final Scheduler scheduler) { *

* * @param other - * the Completable whose termination will cause {@code takeUntil} to emit the item from the source - * Single - * @return a Single that emits the item emitted by the source Single until such time as {@code other} terminates. + * the {@code CompletableSource} whose termination will cause {@code takeUntil} to emit the item from the current + * {@code Single} + * @return the new {@code Single} that emits the item emitted by the current {@code Single} until such time as {@code other} terminates. + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single takeUntil(final CompletableSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final Single takeUntil(@NonNull CompletableSource other) { + Objects.requireNonNull(other, "other is null"); return takeUntil(new CompletableToFlowable(other)); } /** - * Returns a Single that emits the item emitted by the source Single until a Publisher emits an item. Upon + * Returns a {@code Single} that emits the item emitted by the current {@code Single} until a {@link Publisher} emits an item or completes. Upon * emission of an item from {@code other}, this will emit a {@link CancellationException} rather than go to * {@link SingleObserver#onSuccess(Object)}. *

@@ -3807,25 +5198,26 @@ public final Single takeUntil(final CompletableSource other) { *

* * @param other - * the Publisher whose first emitted item will cause {@code takeUntil} to emit the item from the source - * Single + * the {@code Publisher} whose first emitted item or completion will cause {@code takeUntil} to emit {@code CancellationException} + * if the current {@code Single} hasn't completed till then * @param * the type of items emitted by {@code other} - * @return a Single that emits the item emitted by the source Single until such time as {@code other} emits + * @return the new {@code Single} that emits the item emitted by the current {@code Single} until such time as {@code other} emits * its first item + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single takeUntil(final Publisher other) { - ObjectHelper.requireNonNull(other, "other is null"); - return RxJavaPlugins.onAssembly(new SingleTakeUntil(this, other)); + public final <@NonNull E> Single takeUntil(@NonNull Publisher other) { + Objects.requireNonNull(other, "other is null"); + return RxJavaPlugins.onAssembly(new SingleTakeUntil<>(this, other)); } /** - * Returns a Single that emits the item emitted by the source Single until a second Single emits an item. Upon + * Returns a {@code Single} that emits the item emitted by the current {@code Single} until a second {@code Single} emits an item. Upon * emission of an item from {@code other}, this will emit a {@link CancellationException} rather than go to * {@link SingleObserver#onSuccess(Object)}. *

@@ -3836,124 +5228,129 @@ public final Single takeUntil(final Publisher other) { *

* * @param other - * the Single whose emitted item will cause {@code takeUntil} to emit the item from the source Single + * the {@code Single} whose emitted item will cause {@code takeUntil} to emit {@code CancellationException} + * if the current {@code Single} hasn't completed till then * @param * the type of item emitted by {@code other} - * @return a Single that emits the item emitted by the source Single until such time as {@code other} emits its item + * @return the new {@code Single} that emits the item emitted by the current {@code Single} until such time as {@code other} emits its item + * @throws NullPointerException if {@code other} is {@code null} * @see ReactiveX operators documentation: TakeUntil */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.NONE) - public final Single takeUntil(final SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); + public final <@NonNull E> Single takeUntil(@NonNull SingleSource other) { + Objects.requireNonNull(other, "other is null"); return takeUntil(new SingleToFlowable(other)); } /** - * Signals a TimeoutException if the current Single doesn't signal a success value within the + * Signals a {@link TimeoutException} if the current {@code Single} doesn't signal a success value within the * specified timeout window. *

- * + * *

*
Scheduler:
- *
{@code timeout} signals the TimeoutException on the {@code computation} {@link Scheduler}.
+ *
{@code timeout} signals the {@code TimeoutException} on the {@code computation} {@link Scheduler}.
*
* @param timeout the timeout amount * @param unit the time unit - * @return the new Single instance + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Single timeout(long timeout, TimeUnit unit) { + @NonNull + public final Single timeout(long timeout, @NonNull TimeUnit unit) { return timeout0(timeout, unit, Schedulers.computation(), null); } /** - * Signals a TimeoutException if the current Single doesn't signal a success value within the + * Signals a {@link TimeoutException} if the current {@code Single} doesn't signal a success value within the * specified timeout window. *

* *

*
Scheduler:
- *
{@code timeout} signals the TimeoutException on the {@link Scheduler} you specify.
+ *
{@code timeout} signals the {@code TimeoutException} on the {@link Scheduler} you specify.
*
* @param timeout the timeout amount * @param unit the time unit - * @param scheduler the target scheduler where the timeout is awaited and the TimeoutException - * signalled - * @return the new Single instance + * @param scheduler the target {@code Scheduler} where the timeout is awaited and the {@code TimeoutException} + * signaled + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single timeout(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Single timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return timeout0(timeout, unit, scheduler, null); } /** - * Runs the current Single and if it doesn't signal within the specified timeout window, it is - * disposed and the other SingleSource subscribed to. + * Runs the current {@code Single} and if it doesn't signal within the specified timeout window, it is + * disposed and the other {@link SingleSource} subscribed to. *

* *

*
Scheduler:
- *
{@code timeout} subscribes to the other SingleSource on the {@link Scheduler} you specify.
+ *
{@code timeout} subscribes to the other {@code SingleSource} on the {@link Scheduler} you specify.
*
* @param timeout the timeout amount * @param unit the time unit - * @param scheduler the scheduler where the timeout is awaited and the subscription to other happens - * @param other the other SingleSource that gets subscribed to if the current Single times out - * @return the new Single instance + * @param scheduler the {@code Scheduler} where the timeout is awaited and the subscription to other happens + * @param fallback the other {@code SingleSource} that gets subscribed to if the current {@code Single} times out + * @return the new {@code Single} instance + * @throws NullPointerException if {@code unit}, {@code scheduler} or {@code fallback} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single timeout(long timeout, TimeUnit unit, Scheduler scheduler, SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, unit, scheduler, other); + public final Single timeout(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, @NonNull SingleSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, scheduler, fallback); } /** - * Runs the current Single and if it doesn't signal within the specified timeout window, it is - * disposed and the other SingleSource subscribed to. + * Runs the current {@code Single} and if it doesn't signal within the specified timeout window, it is + * disposed and the other {@link SingleSource} subscribed to. *

* *

*
Scheduler:
- *
{@code timeout} subscribes to the other SingleSource on + *
{@code timeout} subscribes to the other {@code SingleSource} on * the {@code computation} {@link Scheduler}.
*
* @param timeout the timeout amount * @param unit the time unit - * @param other the other SingleSource that gets subscribed to if the current Single times out - * @return the new Single instance + * @param fallback the other {@code SingleSource} that gets subscribed to if the current {@code Single} times out + * @return the new {@code Single} instance * @throws NullPointerException - * if other is null, or - * if unit is null, or - * if scheduler is null + * if {@code fallback} or {@code unit} is {@code null} * @since 2.0 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Single timeout(long timeout, TimeUnit unit, SingleSource other) { - ObjectHelper.requireNonNull(other, "other is null"); - return timeout0(timeout, unit, Schedulers.computation(), other); + public final Single timeout(long timeout, @NonNull TimeUnit unit, @NonNull SingleSource fallback) { + Objects.requireNonNull(fallback, "fallback is null"); + return timeout0(timeout, unit, Schedulers.computation(), fallback); } - private Single timeout0(final long timeout, final TimeUnit unit, final Scheduler scheduler, final SingleSource other) { - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new SingleTimeout(this, timeout, unit, scheduler, other)); + private Single timeout0(final long timeout, final TimeUnit unit, final Scheduler scheduler, final SingleSource fallback) { + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleTimeout<>(this, timeout, unit, scheduler, fallback)); } /** * Calls the specified converter function during assembly time and returns its resulting value. *

- * + * *

* This allows fluent conversion to any other type. *

@@ -3962,19 +5359,19 @@ private Single timeout0(final long timeout, final TimeUnit unit, final Schedu *
*

History: 2.1.7 - experimental * @param the resulting object type - * @param converter the function that receives the current Single instance and returns a value + * @param converter the function that receives the current {@code Single} instance and returns a value * @return the converted value - * @throws NullPointerException if converter is null + * @throws NullPointerException if {@code converter} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public final R to(@NonNull SingleConverter converter) { - return ObjectHelper.requireNonNull(converter, "converter is null").apply(this); + return Objects.requireNonNull(converter, "converter is null").apply(this); } /** - * Returns a {@link Completable} that ignores the success value of this {@link Single} + * Returns a {@link Completable} that ignores the success value of this {@code Single} * and signals {@code onComplete} instead. *

* @@ -3983,20 +5380,20 @@ public final R to(@NonNull SingleConverter converter) { *

{@code ignoreElement} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Completable} that signals {@code onComplete} on it's observer when the source {@link Single} - * calls {@code onSuccess}. + * @return the new {@code Completable} instance * @since 2.1.13 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Completable ignoreElement() { - return RxJavaPlugins.onAssembly(new CompletableFromSingle(this)); + return RxJavaPlugins.onAssembly(new CompletableFromSingle<>(this)); } /** - * Converts this Single into a {@link Flowable}. + * Converts this {@code Single} into a {@link Flowable}. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer.
@@ -4004,104 +5401,110 @@ public final Completable ignoreElement() { *
{@code toFlowable} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Flowable} that emits a single item T or an error. + * @return the new {@code Flowable} instance */ @BackpressureSupport(BackpressureKind.FULL) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") + @NonNull public final Flowable toFlowable() { if (this instanceof FuseToFlowable) { return ((FuseToFlowable)this).fuseToFlowable(); } - return RxJavaPlugins.onAssembly(new SingleToFlowable(this)); + return RxJavaPlugins.onAssembly(new SingleToFlowable<>(this)); } /** * Returns a {@link Future} representing the single value emitted by this {@code Single}. *

- * + * + *

+ * Cancelling the {@code Future} will cancel the subscription to the current {@code Single}. *

*
Scheduler:
*
{@code toFuture} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Future} that expects a single item to be emitted by this {@code Single} + * @return the new {@code Future} instance * @see ReactiveX documentation: To */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final Future toFuture() { - return subscribeWith(new FutureSingleObserver()); + return subscribeWith(new FutureMultiObserver<>()); } /** - * Converts this Single into a {@link Maybe}. + * Converts this {@code Single} into a {@link Maybe}. *

- * + * *

*
Scheduler:
*
{@code toMaybe} does not operate by default on a particular {@link Scheduler}.
*
* - * @return a {@link Maybe} that emits a single item T or an error. + * @return the new {@code Maybe} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") + @NonNull public final Maybe toMaybe() { if (this instanceof FuseToMaybe) { return ((FuseToMaybe)this).fuseToMaybe(); } - return RxJavaPlugins.onAssembly(new MaybeFromSingle(this)); + return RxJavaPlugins.onAssembly(new MaybeFromSingle<>(this)); } /** - * Converts this Single into an {@link Observable}. + * Converts this {@code Single} into an {@link Observable}. *

- * + * *

*
Scheduler:
*
{@code toObservable} does not operate by default on a particular {@link Scheduler}.
*
* - * @return an {@link Observable} that emits a single item T or an error. + * @return the new {@code Observable} instance */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @SuppressWarnings("unchecked") + @NonNull public final Observable toObservable() { if (this instanceof FuseToObservable) { return ((FuseToObservable)this).fuseToObservable(); } - return RxJavaPlugins.onAssembly(new SingleToObservable(this)); + return RxJavaPlugins.onAssembly(new SingleToObservable<>(this)); } /** - * Returns a Single which makes sure when a SingleObserver disposes the Disposable, - * that call is propagated up on the specified scheduler. + * Returns a {@code Single} which makes sure when a {@link SingleObserver} disposes the {@link Disposable}, + * that call is propagated up on the specified {@link Scheduler}. *

* *

*
Scheduler:
- *
{@code unsubscribeOn} calls dispose() of the upstream on the {@link Scheduler} you specify.
+ *
{@code unsubscribeOn} calls {@code dispose()} of the upstream on the {@code Scheduler} you specify.
*
*

History: 2.0.9 - experimental * @param scheduler the target scheduler where to execute the disposal - * @return the new Single instance - * @throws NullPointerException if scheduler is null + * @return the new {@code Single} instance + * @throws NullPointerException if {@code scheduler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Single unsubscribeOn(final Scheduler scheduler) { - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new SingleUnsubscribeOn(this, scheduler)); + public final Single unsubscribeOn(@NonNull Scheduler scheduler) { + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new SingleUnsubscribeOn<>(this, scheduler)); } /** - * Returns a Single that emits the result of applying a specified function to the pair of items emitted by - * the source Single and another specified Single. + * Returns a {@code Single} that emits the result of applying a specified function to the pair of items emitted by + * the current {@code Single} and another specified {@link SingleSource}. *

* *

@@ -4110,21 +5513,23 @@ public final Single unsubscribeOn(final Scheduler scheduler) { *
* * @param - * the type of items emitted by the {@code other} Single + * the type of items emitted by the {@code other} {@code Single} * @param - * the type of items emitted by the resulting Single + * the type of items emitted by the resulting {@code Single} * @param other - * the other SingleSource + * the other {@code SingleSource} * @param zipper - * a function that combines the pairs of items from the two SingleSources to generate the items to - * be emitted by the resulting Single - * @return a Single that pairs up values from the source Single and the {@code other} SingleSource + * a function that combines the pairs of items from the two {@code SingleSource}s to generate the items to + * be emitted by the resulting {@code Single} + * @return the new {@code Single} that pairs up values from the current {@code Single} and the {@code other} {@code SingleSource} * and emits the results of {@code zipFunction} applied to these pairs + * @throws NullPointerException if {@code other} or {@code zipper} is {@code null} * @see ReactiveX operators documentation: Zip */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Single zipWith(SingleSource other, BiFunction zipper) { + @NonNull + public final <@NonNull U, @NonNull R> Single zipWith(@NonNull SingleSource other, @NonNull BiFunction zipper) { return zip(this, other, zipper); } @@ -4132,42 +5537,43 @@ public final Single zipWith(SingleSource other, BiFunction * *
*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new TestObserver instance + * @return the new {@code TestObserver} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); subscribe(to); return to; } /** - * Creates a TestObserver optionally in cancelled state, then subscribes it to this Single. + * Creates a {@link TestObserver} optionally in cancelled state, then subscribes it to this {@code Single}. *

* *

*
Scheduler:
*
{@code test} does not operate by default on a particular {@link Scheduler}.
*
- * @param dispose if true, the TestObserver will be cancelled before subscribing to this - * Single. - * @return the new TestObserver instance + * @param dispose if {@code true}, the {@code TestObserver} will be cancelled before subscribing to this + * {@code Single}. + * @return the new {@code TestObserver} instance * @since 2.0 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) + @NonNull public final TestObserver test(boolean dispose) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); if (dispose) { to.dispose(); @@ -4177,7 +5583,182 @@ public final TestObserver test(boolean dispose) { return to; } - private static Single toSingle(Flowable source) { - return RxJavaPlugins.onAssembly(new FlowableSingleSingle(source, null)); + @NonNull + private static Single toSingle(@NonNull Flowable source) { + return RxJavaPlugins.onAssembly(new FlowableSingleSingle<>(source, null)); + } + + // ------------------------------------------------------------------------- + // JDK 8 Support + // ------------------------------------------------------------------------- + + /** + * Signals the completion value or error of the given (hot) {@link CompletionStage}-based asynchronous calculation. + *

+ * + *

+ * Note that the operator takes an already instantiated, running or terminated {@code CompletionStage}. + * If the {@code CompletionStage} is to be created per consumer upon subscription, use {@link #defer(Supplier)} + * around {@code fromCompletionStage}: + *


+     * Single.defer(() -> Single.fromCompletionStage(createCompletionStage()));
+     * 
+ *

+ * If the {@code CompletionStage} completes with {@code null}, the resulting {@code Single} is terminated with + * a {@link NullPointerException}. + *

+ * Canceling the flow can't cancel the execution of the {@code CompletionStage} because {@code CompletionStage} + * itself doesn't support cancellation. Instead, the operator detaches from the {@code CompletionStage}. + *

+ *
Scheduler:
+ *
{@code fromCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code CompletionStage} + * @param stage the {@code CompletionStage} to convert to {@code Single} and signal its success value or error + * @return the new {@code Single} instance + * @throws NullPointerException if {@code stage} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public static <@NonNull T> Single<@NonNull T> fromCompletionStage(@NonNull CompletionStage stage) { + Objects.requireNonNull(stage, "stage is null"); + return RxJavaPlugins.onAssembly(new SingleFromCompletionStage<>(stage)); + } + + /** + * Maps the upstream success value into an {@link Optional} and emits the contained item if not empty as a {@link Maybe}. + *

+ * + * + *

+ *
Scheduler:
+ *
{@code mapOptional} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the non-{@code null} output type + * @param mapper the function that receives the upstream success item and should return a non-empty {@code Optional} + * to emit as the success output or an empty {@code Optional} to complete the {@code Maybe} + * @return the new {@code Maybe} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #map(Function) + * @see #filter(Predicate) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Maybe mapOptional(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleMapOptional<>(this, mapper)); + } + + /** + * Signals the upstream success item (or error) via a {@link CompletionStage}. + *

+ * + *

+ * The upstream can be canceled by converting the resulting {@code CompletionStage} into + * {@link CompletableFuture} via {@link CompletionStage#toCompletableFuture()} and + * calling {@link CompletableFuture#cancel(boolean)} on it. + * The upstream will be also cancelled if the resulting {@code CompletionStage} is converted to and + * completed manually by {@link CompletableFuture#complete(Object)} or {@link CompletableFuture#completeExceptionally(Throwable)}. + *

+ *
Scheduler:
+ *
{@code toCompletionStage} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @return the new {@code CompletionStage} instance + * @since 3.0.0 + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final CompletionStage toCompletionStage() { + return subscribeWith(new CompletionStageConsumer<>(false, null)); + } + + /** + * Maps the upstream succecss value into a Java {@link Stream} and emits its + * items to the downstream consumer as a {@link Flowable}. + * + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flattenAsFlowable(Function)}: + *


+     * source.flattenAsFlowable(item -> createStream(item)::iterator);
+     * 
+ *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flattenStreamAsFlowable(item -> IntStream.rangeClosed(1, 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors backpressure from downstream and iterates the given {@code Stream} + * on demand (i.e., when requested).
+ *
Scheduler:
+ *
{@code flattenStreamAsFlowable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code Stream} and the output {@code Flowable} + * @param mapper the function that receives the upstream success item and should + * return a {@code Stream} of values to emit. + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #flattenAsFlowable(Function) + * @see #flattenStreamAsObservable(Function) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + @NonNull + public final <@NonNull R> Flowable flattenStreamAsFlowable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlattenStreamAsFlowable<>(this, mapper)); + } + + /** + * Maps the upstream succecss value into a Java {@link Stream} and emits its + * items to the downstream consumer as an {@link Observable}. + *

+ * + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flattenAsObservable(Function)}: + *


+     * source.flattenAsObservable(item -> createStream(item)::iterator);
+     * 
+ *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flattenStreamAsObservable(item -> IntStream.rangeClosed(1, 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Scheduler:
+ *
{@code flattenStreamAsObservable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the element type of the {@code Stream} and the output {@code Observable} + * @param mapper the function that receives the upstream success item and should + * return a {@code Stream} of values to emit. + * @return the new {@code Observable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @since 3.0.0 + * @see #flattenAsObservable(Function) + * @see #flattenStreamAsFlowable(Function) + */ + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> Observable flattenStreamAsObservable(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new SingleFlattenStreamAsObservable<>(this, mapper)); } } diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleConverter.java b/src/main/java/io/reactivex/rxjava3/core/SingleConverter.java index 5309dbc160..960a17d9d5 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleConverter.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleConverter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,20 +16,20 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Convenience interface and callback used by the {@link Single#to} operator to turn a Single into another + * Convenience interface and callback used by the {@link Single#to} operator to turn a {@link Single} into another * value fluently. *

History: 2.1.7 - experimental * @param the upstream type * @param the output type * @since 2.2 */ -public interface SingleConverter { +@FunctionalInterface +public interface SingleConverter<@NonNull T, @NonNull R> { /** - * Applies a function to the upstream Single and returns a converted value of type {@code R}. + * Applies a function to the upstream {@link Single} and returns a converted value of type {@code R}. * - * @param upstream the upstream Single instance + * @param upstream the upstream {@code Single} instance * @return the converted value */ - @NonNull R apply(@NonNull Single upstream); } diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleEmitter.java b/src/main/java/io/reactivex/rxjava3/core/SingleEmitter.java index a2302c5f4c..4b18a81644 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleEmitter.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleEmitter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.functions.Cancellable; +import io.reactivex.rxjava3.functions.*; /** * Abstraction over an RxJava {@link SingleObserver} that allows associating @@ -47,7 +47,7 @@ * * @param the value type to emit */ -public interface SingleEmitter { +public interface SingleEmitter<@NonNull T> { /** * Signal a success value. @@ -57,21 +57,23 @@ public interface SingleEmitter { /** * Signal an exception. - * @param t the exception, not null + * @param t the exception, not {@code null} */ void onError(@NonNull Throwable t); /** - * Sets a Disposable on this emitter; any previous Disposable - * or Cancellable will be disposed/cancelled. - * @param d the disposable, null is allowed + * Sets a {@link Disposable} on this emitter; any previous {@code Disposable} + * or {@link Cancellable} will be disposed/cancelled. + *

This method is thread-safe. + * @param d the {@code Disposable}, {@code null} is allowed */ void setDisposable(@Nullable Disposable d); /** * Sets a Cancellable on this emitter; any previous {@link Disposable} * or {@link Cancellable} will be disposed/cancelled. - * @param c the cancellable resource, null is allowed + *

This method is thread-safe. + * @param c the {@code Cancellable} resource, {@code null} is allowed */ void setCancellable(@Nullable Cancellable c); @@ -85,12 +87,12 @@ public interface SingleEmitter { boolean isDisposed(); /** - * Attempts to emit the specified {@code Throwable} error if the downstream + * Attempts to emit the specified {@link Throwable} error if the downstream * hasn't cancelled the sequence or is otherwise terminated, returning false * if the emission is not allowed to happen due to lifecycle restrictions. *

- * Unlike {@link #onError(Throwable)}, the {@code RxJavaPlugins.onError} is not called - * if the error could not be delivered. + * Unlike {@link #onError(Throwable)}, the {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable) RxjavaPlugins.onError} + * is not called if the error could not be delivered. *

History: 2.1.1 - experimental * @param t the throwable error to signal if possible * @return true if successful, false if the downstream is not able to accept further diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleObserver.java b/src/main/java/io/reactivex/rxjava3/core/SingleObserver.java index 778d6feabc..a110d4bcff 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -50,10 +50,10 @@ * the type of item the SingleObserver expects to observe * @since 2.0 */ -public interface SingleObserver { +public interface SingleObserver<@NonNull T> { /** - * Provides the SingleObserver with the means of cancelling (disposing) the + * Provides the {@link SingleObserver} with the means of cancelling (disposing) the * connection (channel) with the Single in both * synchronous (from within {@code onSubscribe(Disposable)} itself) and asynchronous manner. * @param d the Disposable instance whose {@link Disposable#dispose()} can @@ -63,23 +63,23 @@ public interface SingleObserver { void onSubscribe(@NonNull Disposable d); /** - * Notifies the SingleObserver with a single item and that the {@link Single} has finished sending + * Notifies the {@link SingleObserver} with a single item and that the {@link Single} has finished sending * push-based notifications. *

- * The {@link Single} will not call this method if it calls {@link #onError}. + * The {@code Single} will not call this method if it calls {@link #onError}. * * @param t - * the item emitted by the Single + * the item emitted by the {@code Single} */ void onSuccess(@NonNull T t); /** - * Notifies the SingleObserver that the {@link Single} has experienced an error condition. + * Notifies the {@link SingleObserver} that the {@link Single} has experienced an error condition. *

- * If the {@link Single} calls this method, it will not thereafter call {@link #onSuccess}. + * If the {@code Single} calls this method, it will not thereafter call {@link #onSuccess}. * * @param e - * the exception encountered by the Single + * the exception encountered by the {@code Single} */ void onError(@NonNull Throwable e); } diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java index 157a8e42dc..e8b9e89f8b 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleOnSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,22 +10,24 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; /** * A functional interface that has a {@code subscribe()} method that receives - * an instance of a {@link SingleEmitter} instance that allows pushing + * a {@link SingleEmitter} instance that allows pushing * an event in a cancellation-safe manner. * * @param the value type pushed */ -public interface SingleOnSubscribe { +@FunctionalInterface +public interface SingleOnSubscribe<@NonNull T> { /** - * Called for each SingleObserver that subscribes. - * @param emitter the safe emitter instance, never null + * Called for each {@link SingleObserver} that subscribes. + * @param emitter the safe emitter instance, never {@code null} * @throws Throwable on error */ void subscribe(@NonNull SingleEmitter emitter) throws Throwable; diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleOperator.java b/src/main/java/io/reactivex/rxjava3/core/SingleOperator.java index 58ea9d338e..3b9c4a53c5 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleOperator.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleOperator.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,18 +16,19 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to map/wrap a downstream observer to an upstream observer. + * Interface to map/wrap a downstream {@link SingleObserver} to an upstream {@code SingleObserver}. * * @param the value type of the downstream * @param the value type of the upstream */ -public interface SingleOperator { +@FunctionalInterface +public interface SingleOperator<@NonNull Downstream, @NonNull Upstream> { /** - * Applies a function to the child SingleObserver and returns a new parent SingleObserver. - * @param observer the child SingleObserver instance - * @return the parent SingleObserver instance - * @throws Exception on failure + * Applies a function to the child {@link SingleObserver} and returns a new parent {@code SingleObserver}. + * @param observer the child {@code SingleObserver} instance + * @return the parent {@code SingleObserver} instance + * @throws Throwable on failure */ @NonNull - SingleObserver apply(@NonNull SingleObserver observer) throws Exception; + SingleObserver apply(@NonNull SingleObserver observer) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleSource.java b/src/main/java/io/reactivex/rxjava3/core/SingleSource.java index 1a3780c3f6..16147fd55b 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleSource.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.core; import io.reactivex.rxjava3.annotations.NonNull; @@ -24,12 +25,13 @@ * @param the element type * @since 2.0 */ -public interface SingleSource { +@FunctionalInterface +public interface SingleSource<@NonNull T> { /** - * Subscribes the given SingleObserver to this SingleSource instance. - * @param observer the SingleObserver, not null - * @throws NullPointerException if {@code observer} is null + * Subscribes the given {@link SingleObserver} to this {@link SingleSource} instance. + * @param observer the {@code SingleObserver}, not {@code null} + * @throws NullPointerException if {@code observer} is {@code null} */ void subscribe(@NonNull SingleObserver observer); } diff --git a/src/main/java/io/reactivex/rxjava3/core/SingleTransformer.java b/src/main/java/io/reactivex/rxjava3/core/SingleTransformer.java index 080a2d7af1..3ece315f97 100644 --- a/src/main/java/io/reactivex/rxjava3/core/SingleTransformer.java +++ b/src/main/java/io/reactivex/rxjava3/core/SingleTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,17 +16,18 @@ import io.reactivex.rxjava3.annotations.NonNull; /** - * Interface to compose Singles. + * Interface to compose {@link Single}s. * * @param the upstream value type * @param the downstream value type */ -public interface SingleTransformer { +@FunctionalInterface +public interface SingleTransformer<@NonNull Upstream, @NonNull Downstream> { /** - * Applies a function to the upstream Single and returns a SingleSource with + * Applies a function to the upstream {@link Single} and returns a {@link SingleSource} with * optionally different element type. - * @param upstream the upstream Single instance - * @return the transformed SingleSource instance + * @param upstream the upstream {@code Single} instance + * @return the transformed {@code SingleSource} instance */ @NonNull SingleSource apply(@NonNull Single upstream); diff --git a/src/main/java/io/reactivex/rxjava3/core/package-info.java b/src/main/java/io/reactivex/rxjava3/core/package-info.java index 91717d0178..ee76f76a64 100644 --- a/src/main/java/io/reactivex/rxjava3/core/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/core/package-info.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + /** * Base reactive classes: {@link io.reactivex.rxjava3.core.Flowable}, {@link io.reactivex.rxjava3.core.Observable}, * {@link io.reactivex.rxjava3.core.Single}, {@link io.reactivex.rxjava3.core.Maybe} and diff --git a/src/main/java/io/reactivex/rxjava3/disposables/ActionDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/ActionDisposable.java index dd74d600c6..4caad11d79 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/ActionDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/ActionDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.disposables; import io.reactivex.rxjava3.annotations.NonNull; @@ -17,7 +18,7 @@ import io.reactivex.rxjava3.internal.util.ExceptionHelper; /** - * A Disposable container that manages an Action instance. + * A Disposable container that manages an {@link Action} instance. */ final class ActionDisposable extends ReferenceDisposable { @@ -35,4 +36,9 @@ protected void onDisposed(@NonNull Action value) { throw ExceptionHelper.wrapOrThrow(ex); } } + + @Override + public String toString() { + return "ActionDisposable(disposed=" + isDisposed() + ", " + get() + ")"; + } } diff --git a/src/main/java/io/reactivex/rxjava3/disposables/AutoCloseableDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/AutoCloseableDisposable.java new file mode 100644 index 0000000000..34cdaed0eb --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/disposables/AutoCloseableDisposable.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.disposables; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.internal.util.ExceptionHelper; + +/** + * A disposable container that manages an {@link AutoCloseable} instance. + * @since 3.0.0 + */ +final class AutoCloseableDisposable extends ReferenceDisposable { + + private static final long serialVersionUID = -6646144244598696847L; + + AutoCloseableDisposable(AutoCloseable value) { + super(value); + } + + @Override + protected void onDisposed(@NonNull AutoCloseable value) { + try { + value.close(); + } catch (Throwable ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } + } + + @Override + public String toString() { + return "AutoCloseableDisposable(disposed=" + isDisposed() + ", " + get() + ")"; + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/disposables/CompositeDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/CompositeDisposable.java index 245c41305b..bcf28ec148 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/CompositeDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/CompositeDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,18 +10,19 @@ * 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 io.reactivex.rxjava3.disposables; import java.util.*; -import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.*; /** - * A disposable container that can hold onto multiple other disposables and - * offers O(1) add and removal complexity. + * A disposable container that can hold onto multiple other {@link Disposable}s and + * offers O(1) time complexity for {@link #add(Disposable)}, {@link #remove(Disposable)} and {@link #delete(Disposable)} + * operations. */ public final class CompositeDisposable implements Disposable, DisposableContainer { @@ -30,35 +31,35 @@ public final class CompositeDisposable implements Disposable, DisposableContaine volatile boolean disposed; /** - * Creates an empty CompositeDisposable. + * Creates an empty {@code CompositeDisposable}. */ public CompositeDisposable() { } /** - * Creates a CompositeDisposables with the given array of initial elements. - * @param disposables the array of Disposables to start with - * @throws NullPointerException if {@code disposables} or any of its array items is null + * Creates a {@code CompositeDisposable} with the given array of initial {@link Disposable} elements. + * @param disposables the array of {@code Disposable}s to start with + * @throws NullPointerException if {@code disposables} or any of its array items is {@code null} */ public CompositeDisposable(@NonNull Disposable... disposables) { - ObjectHelper.requireNonNull(disposables, "disposables is null"); - this.resources = new OpenHashSet(disposables.length + 1); + Objects.requireNonNull(disposables, "disposables is null"); + this.resources = new OpenHashSet<>(disposables.length + 1); for (Disposable d : disposables) { - ObjectHelper.requireNonNull(d, "A Disposable in the disposables array is null"); + Objects.requireNonNull(d, "A Disposable in the disposables array is null"); this.resources.add(d); } } /** - * Creates a CompositeDisposables with the given Iterable sequence of initial elements. - * @param disposables the Iterable sequence of Disposables to start with - * @throws NullPointerException if {@code disposables} or any of its items is null + * Creates a {@code CompositeDisposable} with the given {@link Iterable} sequence of initial {@link Disposable} elements. + * @param disposables the {@code Iterable} sequence of {@code Disposable} to start with + * @throws NullPointerException if {@code disposables} or any of its items is {@code null} */ public CompositeDisposable(@NonNull Iterable disposables) { - ObjectHelper.requireNonNull(disposables, "disposables is null"); - this.resources = new OpenHashSet(); + Objects.requireNonNull(disposables, "disposables is null"); + this.resources = new OpenHashSet<>(); for (Disposable d : disposables) { - ObjectHelper.requireNonNull(d, "A Disposable item in the disposables sequence is null"); + Objects.requireNonNull(d, "A Disposable item in the disposables sequence is null"); this.resources.add(d); } } @@ -87,21 +88,21 @@ public boolean isDisposed() { } /** - * Adds a disposable to this container or disposes it if the + * Adds a {@link Disposable} to this container or disposes it if the * container has been disposed. - * @param disposable the disposable to add, not null - * @return true if successful, false if this container has been disposed - * @throws NullPointerException if {@code disposable} is null + * @param disposable the {@code Disposable} to add, not {@code null} + * @return {@code true} if successful, {@code false} if this container has been disposed + * @throws NullPointerException if {@code disposable} is {@code null} */ @Override public boolean add(@NonNull Disposable disposable) { - ObjectHelper.requireNonNull(disposable, "disposable is null"); + Objects.requireNonNull(disposable, "disposable is null"); if (!disposed) { synchronized (this) { if (!disposed) { OpenHashSet set = resources; if (set == null) { - set = new OpenHashSet(); + set = new OpenHashSet<>(); resources = set; } set.add(disposable); @@ -114,24 +115,24 @@ public boolean add(@NonNull Disposable disposable) { } /** - * Atomically adds the given array of Disposables to the container or + * Atomically adds the given array of {@link Disposable}s to the container or * disposes them all if the container has been disposed. - * @param disposables the array of Disposables - * @return true if the operation was successful, false if the container has been disposed - * @throws NullPointerException if {@code disposables} or any of its array items is null + * @param disposables the array of {@code Disposable}s + * @return {@code true} if the operation was successful, {@code false} if the container has been disposed + * @throws NullPointerException if {@code disposables} or any of its array items is {@code null} */ public boolean addAll(@NonNull Disposable... disposables) { - ObjectHelper.requireNonNull(disposables, "disposables is null"); + Objects.requireNonNull(disposables, "disposables is null"); if (!disposed) { synchronized (this) { if (!disposed) { OpenHashSet set = resources; if (set == null) { - set = new OpenHashSet(disposables.length + 1); + set = new OpenHashSet<>(disposables.length + 1); resources = set; } for (Disposable d : disposables) { - ObjectHelper.requireNonNull(d, "A Disposable in the disposables array is null"); + Objects.requireNonNull(d, "A Disposable in the disposables array is null"); set.add(d); } return true; @@ -145,10 +146,11 @@ public boolean addAll(@NonNull Disposable... disposables) { } /** - * Removes and disposes the given disposable if it is part of this + * Removes and disposes the given {@link Disposable} if it is part of this * container. - * @param disposable the disposable to remove and dispose, not null - * @return true if the operation was successful + * @param disposable the disposable to remove and dispose, not {@code null} + * @return {@code true} if the operation was successful + * @throws NullPointerException if {@code disposable} is {@code null} */ @Override public boolean remove(@NonNull Disposable disposable) { @@ -160,15 +162,15 @@ public boolean remove(@NonNull Disposable disposable) { } /** - * Removes (but does not dispose) the given disposable if it is part of this + * Removes (but does not dispose) the given {@link Disposable} if it is part of this * container. - * @param disposable the disposable to remove, not null - * @return true if the operation was successful - * @throws NullPointerException if {@code disposable} is null + * @param disposable the disposable to remove, not {@code null} + * @return {@code true} if the operation was successful + * @throws NullPointerException if {@code disposable} is {@code null} */ @Override public boolean delete(@NonNull Disposable disposable) { - ObjectHelper.requireNonNull(disposable, "disposables is null"); + Objects.requireNonNull(disposable, "disposable is null"); if (disposed) { return false; } @@ -186,7 +188,7 @@ public boolean delete(@NonNull Disposable disposable) { } /** - * Atomically clears the container, then disposes all the previously contained Disposables. + * Atomically clears the container, then disposes all the previously contained {@link Disposable}s. */ public void clear() { if (disposed) { @@ -206,8 +208,8 @@ public void clear() { } /** - * Returns the number of currently held Disposables. - * @return the number of currently held Disposables + * Returns the number of currently held {@link Disposable}s. + * @return the number of currently held {@code Disposable}s */ public int size() { if (disposed) { @@ -223,11 +225,11 @@ public int size() { } /** - * Dispose the contents of the OpenHashSet by suppressing non-fatal - * Throwables till the end. - * @param set the OpenHashSet to dispose elements of + * Dispose the contents of the {@link OpenHashSet} by suppressing non-fatal + * {@link Throwable}s till the end. + * @param set the {@code OpenHashSet} to dispose elements of */ - void dispose(OpenHashSet set) { + void dispose(@Nullable OpenHashSet set) { if (set == null) { return; } @@ -240,7 +242,7 @@ void dispose(OpenHashSet set) { } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (errors == null) { - errors = new ArrayList(); + errors = new ArrayList<>(); } errors.add(ex); } diff --git a/src/main/java/io/reactivex/rxjava3/disposables/Disposable.java b/src/main/java/io/reactivex/rxjava3/disposables/Disposable.java index 2fab6e83ed..845d603171 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/Disposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/Disposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,18 @@ * 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 io.reactivex.rxjava3.disposables; +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.internal.functions.Functions; +import org.reactivestreams.Subscription; + +import java.util.Objects; +import java.util.concurrent.Future; + /** * Represents a disposable resource. */ @@ -26,4 +36,126 @@ public interface Disposable { * @return true if this resource has been disposed */ boolean isDisposed(); + + /** + * Construct a {@code Disposable} by wrapping a {@link Runnable} that is + * executed exactly once when the {@code Disposable} is disposed. + * @param run the {@code Runnable} to wrap + * @return the new {@code Disposable} instance + * @throws NullPointerException if {@code run} is {@code null} + * @since 3.0.0 + */ + @NonNull + static Disposable fromRunnable(@NonNull Runnable run) { + Objects.requireNonNull(run, "run is null"); + return new RunnableDisposable(run); + } + + /** + * Construct a {@code Disposable} by wrapping a {@link Action} that is + * executed exactly once when the {@code Disposable} is disposed. + * @param action the {@code Action} to wrap + * @return the new {@code Disposable} instance + * @throws NullPointerException if {@code action} is {@code null} + * @since 3.0.0 + */ + @NonNull + static Disposable fromAction(@NonNull Action action) { + Objects.requireNonNull(action, "action is null"); + return new ActionDisposable(action); + } + + /** + * Construct a {@code Disposable} by wrapping a {@link Future} that is + * cancelled exactly once when the {@code Disposable} is disposed. + *

+ * The {@code Future} is cancelled with {@code mayInterruptIfRunning == true}. + * @param future the {@code Future} to wrap + * @return the new {@code Disposable} instance + * @throws NullPointerException if {@code future} is {@code null} + * @see #fromFuture(Future, boolean) + * @since 3.0.0 + */ + @NonNull + static Disposable fromFuture(@NonNull Future future) { + Objects.requireNonNull(future, "future is null"); + return fromFuture(future, true); + } + + /** + * Construct a {@code Disposable} by wrapping a {@link Future} that is + * cancelled exactly once when the {@code Disposable} is disposed. + * @param future the {@code Future} to wrap + * @param allowInterrupt if true, the future cancel happens via {@code Future.cancel(true)} + * @return the new {@code Disposable} instance + * @throws NullPointerException if {@code future} is {@code null} + * @since 3.0.0 + */ + @NonNull + static Disposable fromFuture(@NonNull Future future, boolean allowInterrupt) { + Objects.requireNonNull(future, "future is null"); + return new FutureDisposable(future, allowInterrupt); + } + + /** + * Construct a {@code Disposable} by wrapping a {@link Subscription} that is + * cancelled exactly once when the {@code Disposable} is disposed. + * @param subscription the {@code Runnable} to wrap + * @return the new {@code Disposable} instance + * @throws NullPointerException if {@code subscription} is {@code null} + * @since 3.0.0 + */ + @NonNull + static Disposable fromSubscription(@NonNull Subscription subscription) { + Objects.requireNonNull(subscription, "subscription is null"); + return new SubscriptionDisposable(subscription); + } + + /** + * Construct a {@code Disposable} by wrapping an {@link AutoCloseable} that is + * closed exactly once when the {@code Disposable} is disposed. + * @param autoCloseable the {@code AutoCloseable} to wrap + * @return the new {@code Disposable} instance + * @throws NullPointerException if {@code autoCloseable} is {@code null} + * @since 3.0.0 + */ + @NonNull + static Disposable fromAutoCloseable(@NonNull AutoCloseable autoCloseable) { + Objects.requireNonNull(autoCloseable, "autoCloseable is null"); + return new AutoCloseableDisposable(autoCloseable); + } + + /** + * Construct an {@link AutoCloseable} by wrapping a {@code Disposable} that is + * disposed when the returned {@code AutoCloseable} is closed. + * @param disposable the {@code Disposable} instance + * @return the new {@code AutoCloseable} instance + * @throws NullPointerException if {@code disposable} is {@code null} + * @since 3.0.0 + */ + @NonNull + static AutoCloseable toAutoCloseable(@NonNull Disposable disposable) { + Objects.requireNonNull(disposable, "disposable is null"); + return disposable::dispose; + } + + /** + * Returns a new, non-disposed {@code Disposable} instance. + * @return a new, non-disposed {@code Disposable} instance + * @since 3.0.0 + */ + @NonNull + static Disposable empty() { + return fromRunnable(Functions.EMPTY_RUNNABLE); + } + + /** + * Returns a shared, disposed {@code Disposable} instance. + * @return a shared, disposed {@code Disposable} instance + * @since 3.0.0 + */ + @NonNull + static Disposable disposed() { + return EmptyDisposable.INSTANCE; + } } diff --git a/src/main/java/io/reactivex/rxjava3/disposables/DisposableContainer.java b/src/main/java/io/reactivex/rxjava3/disposables/DisposableContainer.java index d7099fbadc..49311e4082 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/DisposableContainer.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/DisposableContainer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/disposables/Disposables.java b/src/main/java/io/reactivex/rxjava3/disposables/Disposables.java deleted file mode 100644 index d566b744f4..0000000000 --- a/src/main/java/io/reactivex/rxjava3/disposables/Disposables.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * 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. - */ - -package io.reactivex.rxjava3.disposables; - -import java.util.concurrent.Future; - -import org.reactivestreams.Subscription; - -import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.functions.Action; -import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.*; - -/** - * Utility class to help create disposables by wrapping - * other types. - * @since 2.0 - */ -public final class Disposables { - /** Utility class. */ - private Disposables() { - throw new IllegalStateException("No instances!"); - } - - /** - * Construct a Disposable by wrapping a Runnable that is - * executed exactly once when the Disposable is disposed. - * @param run the Runnable to wrap - * @return the new Disposable instance - */ - @NonNull - public static Disposable fromRunnable(@NonNull Runnable run) { - ObjectHelper.requireNonNull(run, "run is null"); - return new RunnableDisposable(run); - } - - /** - * Construct a Disposable by wrapping a Action that is - * executed exactly once when the Disposable is disposed. - * @param run the Action to wrap - * @return the new Disposable instance - */ - @NonNull - public static Disposable fromAction(@NonNull Action run) { - ObjectHelper.requireNonNull(run, "run is null"); - return new ActionDisposable(run); - } - - /** - * Construct a Disposable by wrapping a Future that is - * cancelled exactly once when the Disposable is disposed. - * @param future the Future to wrap - * @return the new Disposable instance - */ - @NonNull - public static Disposable fromFuture(@NonNull Future future) { - ObjectHelper.requireNonNull(future, "future is null"); - return fromFuture(future, true); - } - - /** - * Construct a Disposable by wrapping a Future that is - * cancelled exactly once when the Disposable is disposed. - * @param future the Future to wrap - * @param allowInterrupt if true, the future cancel happens via Future.cancel(true) - * @return the new Disposable instance - */ - @NonNull - public static Disposable fromFuture(@NonNull Future future, boolean allowInterrupt) { - ObjectHelper.requireNonNull(future, "future is null"); - return new FutureDisposable(future, allowInterrupt); - } - - /** - * Construct a Disposable by wrapping a Subscription that is - * cancelled exactly once when the Disposable is disposed. - * @param subscription the Runnable to wrap - * @return the new Disposable instance - */ - @NonNull - public static Disposable fromSubscription(@NonNull Subscription subscription) { - ObjectHelper.requireNonNull(subscription, "subscription is null"); - return new SubscriptionDisposable(subscription); - } - - /** - * Returns a new, non-disposed Disposable instance. - * @return a new, non-disposed Disposable instance - */ - @NonNull - public static Disposable empty() { - return fromRunnable(Functions.EMPTY_RUNNABLE); - } - - /** - * Returns a disposed Disposable instance. - * @return a disposed Disposable instance - */ - @NonNull - public static Disposable disposed() { - return EmptyDisposable.INSTANCE; - } -} diff --git a/src/main/java/io/reactivex/rxjava3/disposables/FutureDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/FutureDisposable.java index 0a900f6ed1..d8f70ff655 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/FutureDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/FutureDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,13 +10,14 @@ * 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 io.reactivex.rxjava3.disposables; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; /** - * A Disposable container that cancels a Future instance. + * A Disposable container that cancels a {@link Future} instance. */ final class FutureDisposable extends AtomicReference> implements Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/disposables/ReferenceDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/ReferenceDisposable.java index e5032d694b..55bb24a268 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/ReferenceDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/ReferenceDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,10 +13,10 @@ package io.reactivex.rxjava3.disposables; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Base class for Disposable containers that manage some other type that @@ -29,7 +29,7 @@ abstract class ReferenceDisposable extends AtomicReference implements Disp private static final long serialVersionUID = 6537757548749041217L; ReferenceDisposable(T value) { - super(ObjectHelper.requireNonNull(value, "value is null")); + super(Objects.requireNonNull(value, "value is null")); } protected abstract void onDisposed(@NonNull T value); diff --git a/src/main/java/io/reactivex/rxjava3/disposables/RunnableDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/RunnableDisposable.java index 43eee3105c..390a7912b4 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/RunnableDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/RunnableDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,12 +10,13 @@ * 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 io.reactivex.rxjava3.disposables; import io.reactivex.rxjava3.annotations.NonNull; /** - * A disposable container that manages a Runnable instance. + * A disposable container that manages a {@link Runnable} instance. */ final class RunnableDisposable extends ReferenceDisposable { diff --git a/src/main/java/io/reactivex/rxjava3/disposables/SerialDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/SerialDisposable.java index 187a94b1e5..7b1f6463a8 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/SerialDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/SerialDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public final class SerialDisposable implements Disposable { * Constructs an empty SerialDisposable. */ public SerialDisposable() { - this.resource = new AtomicReference(); + this.resource = new AtomicReference<>(); } /** @@ -38,7 +38,7 @@ public SerialDisposable() { * @param initialDisposable the initial Disposable instance to use, null allowed */ public SerialDisposable(@Nullable Disposable initialDisposable) { - this.resource = new AtomicReference(initialDisposable); + this.resource = new AtomicReference<>(initialDisposable); } /** @@ -71,7 +71,7 @@ public boolean replace(@Nullable Disposable next) { public Disposable get() { Disposable d = resource.get(); if (d == DisposableHelper.DISPOSED) { - return Disposables.disposed(); + return Disposable.disposed(); } return d; } diff --git a/src/main/java/io/reactivex/rxjava3/disposables/SubscriptionDisposable.java b/src/main/java/io/reactivex/rxjava3/disposables/SubscriptionDisposable.java index f6756b58de..48c5c7ab3d 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/SubscriptionDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/SubscriptionDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.disposables; import org.reactivestreams.Subscription; diff --git a/src/main/java/io/reactivex/rxjava3/disposables/package-info.java b/src/main/java/io/reactivex/rxjava3/disposables/package-info.java index fc3c4221b9..00ff5dd6ff 100644 --- a/src/main/java/io/reactivex/rxjava3/disposables/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/disposables/package-info.java @@ -1,22 +1,19 @@ -/** +/* * 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. */ /** - * Default implementations for Disposable-based resource management - * (Disposable container types) and utility classes to construct - * Disposables from callbacks and other types. + * Default implementations for {@link io.reactivex.rxjava3.disposables.Disposable Disposable}-based resource management + * ({@code Disposable} container types) and utility classes to construct + * {@link io.reactivex.rxjava3.disposables.Disposable Disposables} from callbacks and other types. */ package io.reactivex.rxjava3.disposables; diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java b/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java index aa08128917..5d2928b28f 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/CompositeException.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.exceptions; import java.io.*; @@ -62,8 +60,7 @@ public CompositeException(@NonNull Throwable... exceptions) { * @throws IllegalArgumentException if errors is empty. */ public CompositeException(@NonNull Iterable errors) { - Set deDupedExceptions = new LinkedHashSet(); - List localExceptions = new ArrayList(); + Set deDupedExceptions = new LinkedHashSet<>(); if (errors != null) { for (Throwable ex : errors) { if (ex instanceof CompositeException) { @@ -81,7 +78,7 @@ public CompositeException(@NonNull Iterable errors) { if (deDupedExceptions.isEmpty()) { throw new IllegalArgumentException("errors is empty"); } - localExceptions.addAll(deDupedExceptions); + List localExceptions = new ArrayList<>(deDupedExceptions); this.exceptions = Collections.unmodifiableList(localExceptions); this.message = exceptions.size() + " exceptions occurred. "; } @@ -108,7 +105,7 @@ public synchronized Throwable getCause() { // NOPMD if (cause == null) { String separator = System.getProperty("line.separator"); if (exceptions.size() > 1) { - Map seenCauses = new IdentityHashMap(); + Map seenCauses = new IdentityHashMap<>(); StringBuilder aggregateMessage = new StringBuilder(); aggregateMessage.append("Multiple exceptions (").append(exceptions.size()).append(")").append(separator); @@ -202,38 +199,41 @@ public void printStackTrace(PrintWriter s) { * Special handling for printing out a {@code CompositeException}. * Loops through all inner exceptions and prints them out. * - * @param s + * @param output * stream to print to */ - private void printStackTrace(PrintStreamOrWriter s) { - StringBuilder b = new StringBuilder(128); - b.append(this).append('\n'); + private void printStackTrace(PrintStreamOrWriter output) { + output.append(this).append("\n"); for (StackTraceElement myStackElement : getStackTrace()) { - b.append("\tat ").append(myStackElement).append('\n'); + output.append("\tat ").append(myStackElement).append("\n"); } int i = 1; for (Throwable ex : exceptions) { - b.append(" ComposedException ").append(i).append(" :\n"); - appendStackTrace(b, ex, "\t"); + output.append(" ComposedException ").append(i).append(" :\n"); + appendStackTrace(output, ex, "\t"); i++; } - s.println(b.toString()); + output.append("\n"); } - private void appendStackTrace(StringBuilder b, Throwable ex, String prefix) { - b.append(prefix).append(ex).append('\n'); + private void appendStackTrace(PrintStreamOrWriter output, Throwable ex, String prefix) { + output.append(prefix).append(ex).append('\n'); for (StackTraceElement stackElement : ex.getStackTrace()) { - b.append("\t\tat ").append(stackElement).append('\n'); + output.append("\t\tat ").append(stackElement).append('\n'); } if (ex.getCause() != null) { - b.append("\tCaused by: "); - appendStackTrace(b, ex.getCause(), ""); + output.append("\tCaused by: "); + appendStackTrace(output, ex.getCause(), ""); } } abstract static class PrintStreamOrWriter { - /** Prints the specified string as a line on this StreamOrWriter. */ - abstract void println(Object o); + /** + * Prints the object's string representation via the underlying PrintStream or PrintWriter. + * @param o the object to print + * @return this + */ + abstract PrintStreamOrWriter append(Object o); } /** @@ -247,11 +247,15 @@ static final class WrappedPrintStream extends PrintStreamOrWriter { } @Override - void println(Object o) { - printStream.println(o); + WrappedPrintStream append(Object o) { + printStream.print(o); + return this; } } + /** + * Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation. + */ static final class WrappedPrintWriter extends PrintStreamOrWriter { private final PrintWriter printWriter; @@ -260,8 +264,9 @@ static final class WrappedPrintWriter extends PrintStreamOrWriter { } @Override - void println(Object o) { - printWriter.println(o); + WrappedPrintWriter append(Object o) { + printWriter.print(o); + return this; } } diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/Exceptions.java b/src/main/java/io/reactivex/rxjava3/exceptions/Exceptions.java index 41ddbd4fed..155a2801d2 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/Exceptions.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/Exceptions.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/MissingBackpressureException.java b/src/main/java/io/reactivex/rxjava3/exceptions/MissingBackpressureException.java index f7b19a2fc2..f0a173ba59 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/MissingBackpressureException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/MissingBackpressureException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,6 +20,15 @@ public final class MissingBackpressureException extends RuntimeException { private static final long serialVersionUID = 8517344746016032542L; + /** + * The default error message. + *

+ * This can happen if the downstream doesn't call {@link org.reactivestreams.Subscription#request(long)} + * in time or at all. + * @since 3.1.6 + */ + public static final String DEFAULT_MESSAGE = "Could not emit value due to lack of requests"; + /** * Constructs a MissingBackpressureException without message or cause. */ @@ -35,4 +44,13 @@ public MissingBackpressureException(String message) { super(message); } + /** + * Constructs a new {@code MissingBackpressureException} with the + * default message {@value #DEFAULT_MESSAGE}. + * @return the new {@code MissingBackpressureException} instance. + * @since 3.1.6 + */ + public static MissingBackpressureException createDefault() { + return new MissingBackpressureException(DEFAULT_MESSAGE); + } } diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedException.java b/src/main/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedException.java index d70769af39..072b889a57 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -50,4 +50,4 @@ public OnErrorNotImplementedException(String message, @NonNull Throwable e) { public OnErrorNotImplementedException(@NonNull Throwable e) { this("The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | " + e, e); } -} \ No newline at end of file +} diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/ProtocolViolationException.java b/src/main/java/io/reactivex/rxjava3/exceptions/ProtocolViolationException.java index 9df2ed9762..66fab7f911 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/ProtocolViolationException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/ProtocolViolationException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/QueueOverflowException.java b/src/main/java/io/reactivex/rxjava3/exceptions/QueueOverflowException.java new file mode 100644 index 0000000000..bdd8a25e6f --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/exceptions/QueueOverflowException.java @@ -0,0 +1,49 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.exceptions; + +/** + * Indicates an overflow happened because the upstream disregarded backpressure completely or + * {@link org.reactivestreams.Subscriber#onNext(Object)} was called concurrently from multiple threads + * without synchronization. Rarely, it is an indication of bugs inside an operator. + * @since 3.1.6 + */ +public final class QueueOverflowException extends RuntimeException { + + private static final long serialVersionUID = 8517344746016032542L; + + /** + * The message for queue overflows. + *

+ * This can happen if the upstream disregards backpressure completely or calls + * {@link org.reactivestreams.Subscriber#onNext(Object)} concurrently from multiple threads + * without synchronization. Rarely, it is an indication of bugs inside an operator. + */ + private static final String DEFAULT_MESSAGE = "Queue overflow due to illegal concurrent onNext calls or a bug in an operator"; + + /** + * Constructs a QueueOverflowException with the default message. + */ + public QueueOverflowException() { + this(DEFAULT_MESSAGE); + } + + /** + * Constructs a QueueOverflowException with the given message but no cause. + * @param message the error message + */ + public QueueOverflowException(String message) { + super(message); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/UndeliverableException.java b/src/main/java/io/reactivex/rxjava3/exceptions/UndeliverableException.java index 6c84920e74..11a4c07f8a 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/UndeliverableException.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/UndeliverableException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,7 +14,7 @@ package io.reactivex.rxjava3.exceptions; /** - * Wrapper for Throwable errors that are sent to `RxJavaPlugins.onError`. + * Wrapper for Throwable errors that are sent to {@link io.reactivex.rxjava3.plugins.RxJavaPlugins#onError(Throwable) RxJavaPlugins.onError}. *

History: 2.0.6 - experimental; 2.1 - beta * @since 2.2 */ diff --git a/src/main/java/io/reactivex/rxjava3/exceptions/package-info.java b/src/main/java/io/reactivex/rxjava3/exceptions/package-info.java index 1b3340e414..91de82962f 100644 --- a/src/main/java/io/reactivex/rxjava3/exceptions/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/exceptions/package-info.java @@ -1,21 +1,21 @@ -/** +/* * 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. */ /** - * Exception handling utilities, safe subscriber exception classes, - * lifecycle exception classes. + * Exception handling utilities ({@link io.reactivex.rxjava3.exceptions.Exceptions Exceptions}), + * composite exception container ({@link io.reactivex.rxjava3.exceptions.CompositeException CompositeException}) and + * various lifecycle-related ({@link io.reactivex.rxjava3.exceptions.MissingBackpressureException UndeliverableException}) + * and behavior-violation exception types ({@link io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException OnErrorNotImplementedException}, + * {@link io.reactivex.rxjava3.exceptions.MissingBackpressureException MissingBackpressureException}). */ package io.reactivex.rxjava3.exceptions; diff --git a/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java b/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java index 8b09993ac2..133d0186c8 100644 --- a/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/flowables/ConnectableFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,9 +13,10 @@ package io.reactivex.rxjava3.flowables; +import java.util.Objects; import java.util.concurrent.TimeUnit; -import org.reactivestreams.Subscriber; +import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; @@ -33,12 +34,12 @@ * can wait for all intended {@link Subscriber}s to {@link Flowable#subscribe} to the {@code Flowable} * before the {@code Flowable} begins emitting items. *

- * + * *

* When the upstream terminates, the {@code ConnectableFlowable} remains in this terminated state and, - * depending on the actual underlying implementation, relays cached events to late {@link Subscriber}s. + * depending on the actual underlying implementation, relays cached events to late {@code Subscriber}s. * In order to reuse and restart this {@code ConnectableFlowable}, the {@link #reset()} method has to be called. - * When called, this {@code ConnectableFlowable} will appear as fresh, unconnected source to new {@link Subscriber}s. + * When called, this {@code ConnectableFlowable} will appear as fresh, unconnected source to new {@code Subscriber}s. * Disposing the connection will reset the {@code ConnectableFlowable} to its fresh state and there is no need to call * {@code reset()} in this case. *

@@ -47,8 +48,7 @@ * there is no unwanted signal loss due to early {@code connect()} or {@code reset()} calls while {@code Subscriber}s are * still being subscribed to to this {@code ConnectableFlowable} to receive signals from the get go. *

- * @see RxJava Wiki: - * Connectable Observable Operators + * @see RxJava Wiki: Connectable Observable Operators * @param * the type of items emitted by the {@code ConnectableFlowable} * @since 2.0.0 @@ -58,20 +58,31 @@ public abstract class ConnectableFlowable extends Flowable { /** * Instructs the {@code ConnectableFlowable} to begin emitting the items from its underlying * {@link Flowable} to its {@link Subscriber}s. + *

+ *
Scheduler:
+ *
The behavior is determined by the implementor of this abstract class.
+ *
* * @param connection * the action that receives the connection subscription before the subscription to source happens * allowing the caller to synchronously disconnect a synchronous source + * @throws NullPointerException if {@code connection} is {@code null} * @see ReactiveX documentation: Connect */ + @SchedulerSupport(SchedulerSupport.NONE) public abstract void connect(@NonNull Consumer connection); /** - * Resets this ConnectableFlowable into its fresh state if it has terminated. + * Resets this {@code ConnectableFlowable} into its fresh state if it has terminated. *

* Calling this method on a fresh or active {@code ConnectableFlowable} has no effect. + *

+ *
Scheduler:
+ *
The behavior is determined by the implementor of this abstract class.
+ *
* @since 3.0.0 */ + @SchedulerSupport(SchedulerSupport.NONE) public abstract void reset(); /** @@ -79,10 +90,16 @@ public abstract class ConnectableFlowable extends Flowable { * {@link Flowable} to its {@link Subscriber}s. *

* To disconnect from a synchronous source, use the {@link #connect(io.reactivex.rxjava3.functions.Consumer)} method. + *

+ *
Scheduler:
+ *
The behavior is determined by the implementor of this abstract class.
+ *
* * @return the subscription representing the connection * @see ReactiveX documentation: Connect */ + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) public final Disposable connect() { ConnectConsumer cc = new ConnectConsumer(); connect(cc); @@ -90,7 +107,7 @@ public final Disposable connect() { } /** - * Returns a {@code Flowable} that stays connected to this {@code ConnectableFlowable} as long as there + * Returns a {@link Flowable} that stays connected to this {@code ConnectableFlowable} as long as there * is at least one subscription to this {@code ConnectableFlowable}. *
*
Backpressure:
@@ -99,7 +116,7 @@ public final Disposable connect() { *
Scheduler:
*
This {@code refCount} overload does not operate on any particular {@link Scheduler}.
*
- * @return a {@link Flowable} + * @return the new {@code Flowable} instance * @see ReactiveX documentation: RefCount * @see #refCount(int) * @see #refCount(long, TimeUnit) @@ -110,7 +127,7 @@ public final Disposable connect() { @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.PASS_THROUGH) public Flowable refCount() { - return RxJavaPlugins.onAssembly(new FlowableRefCount(this)); + return RxJavaPlugins.onAssembly(new FlowableRefCount<>(this)); } /** @@ -125,12 +142,14 @@ public Flowable refCount() { *
*

History: 2.1.14 - experimental * @param subscriberCount the number of subscribers required to connect to the upstream - * @return the new Flowable instance + * @return the new {@link Flowable} instance + * @throws IllegalArgumentException if {@code subscriberCount} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @NonNull public final Flowable refCount(int subscriberCount) { return refCount(subscriberCount, 0, TimeUnit.NANOSECONDS, Schedulers.trampoline()); } @@ -149,14 +168,16 @@ public final Flowable refCount(int subscriberCount) { *

History: 2.1.14 - experimental * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout - * @return the new Flowable instance + * @return the new {@link Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see #refCount(long, TimeUnit, Scheduler) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) @BackpressureSupport(BackpressureKind.PASS_THROUGH) - public final Flowable refCount(long timeout, TimeUnit unit) { + @NonNull + public final Flowable refCount(long timeout, @NonNull TimeUnit unit) { return refCount(1, timeout, unit, Schedulers.computation()); } @@ -175,13 +196,15 @@ public final Flowable refCount(long timeout, TimeUnit unit) { * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Flowable instance + * @return the new {@link Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) @BackpressureSupport(BackpressureKind.PASS_THROUGH) - public final Flowable refCount(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable refCount(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return refCount(1, timeout, unit, scheduler); } @@ -200,14 +223,17 @@ public final Flowable refCount(long timeout, TimeUnit unit, Scheduler schedul * @param subscriberCount the number of subscribers required to connect to the upstream * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout - * @return the new Flowable instance + * @return the new {@link Flowable} instance + * @throws NullPointerException if {@code unit} is {@code null} + * @throws IllegalArgumentException if {@code subscriberCount} is non-positive * @see #refCount(int, long, TimeUnit, Scheduler) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) @BackpressureSupport(BackpressureKind.PASS_THROUGH) - public final Flowable refCount(int subscriberCount, long timeout, TimeUnit unit) { + @NonNull + public final Flowable refCount(int subscriberCount, long timeout, @NonNull TimeUnit unit) { return refCount(subscriberCount, timeout, unit, Schedulers.computation()); } @@ -227,99 +253,134 @@ public final Flowable refCount(int subscriberCount, long timeout, TimeUnit un * @param timeout the time to wait before disconnecting after all subscribers unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Flowable instance + * @return the new {@link Flowable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code subscriberCount} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) @BackpressureSupport(BackpressureKind.PASS_THROUGH) - public final Flowable refCount(int subscriberCount, long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Flowable refCount(int subscriberCount, long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { ObjectHelper.verifyPositive(subscriberCount, "subscriberCount"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new FlowableRefCount(this, subscriberCount, timeout, unit, scheduler)); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new FlowableRefCount<>(this, subscriberCount, timeout, unit, scheduler)); } /** - * Returns a Flowable that automatically connects (at most once) to this ConnectableFlowable - * when the first Subscriber subscribes. + * Returns a {@link Flowable} that automatically connects (at most once) to this {@code ConnectableFlowable} + * when the first {@link Subscriber} subscribes. *

* *

* The connection happens after the first subscription and happens at most once - * during the lifetime of the returned Flowable. If this ConnectableFlowable - * terminates, the connection is never renewed, no matter how Subscribers come + * during the lifetime of the returned {@code Flowable}. If this {@code ConnectableFlowable} + * terminates, the connection is never renewed, no matter how {@code Subscriber}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Subscriber}s have cancelled their {@code Subscription}s. + * connection when all {@code Subscriber}s have cancelled their {@link Subscription}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload - * to gain access to the {@code Disposable} representing the only connection. + * to gain access to the {@link Disposable} representing the only connection. + *

+ *
Backpressure:
+ *
The operator itself doesn't interfere with backpressure which is determined by + * the upstream {@code ConnectableFlowable}'s behavior.
+ *
Scheduler:
+ *
{@code autoConnect} does not operate by default on a particular {@link Scheduler}.
+ *
* - * @return a Flowable that automatically connects to this ConnectableFlowable - * when the first Subscriber subscribes + * @return a new {@code Flowable} instance that automatically connects to this {@code ConnectableFlowable} + * when the first {@code Subscriber} subscribes * @see #refCount() * @see #autoConnect(int, Consumer) */ @NonNull + @CheckReturnValue + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public Flowable autoConnect() { return autoConnect(1); } /** - * Returns a Flowable that automatically connects (at most once) to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it. + * Returns a {@link Flowable} that automatically connects (at most once) to this {@code ConnectableFlowable} + * when the specified number of {@link Subscriber}s subscribe to it. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Flowable. If this ConnectableFlowable - * terminates, the connection is never renewed, no matter how Subscribers come + * during the lifetime of the returned {@code Flowable}. If this {@code ConnectableFlowable} + * terminates, the connection is never renewed, no matter how {@code Subscriber}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Subscriber}s have cancelled their {@code Subscription}s. + * connection when all {@code Subscriber}s have cancelled their {@link Subscription}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload - * to gain access to the {@code Disposable} representing the only connection. + * to gain access to the {@link Disposable} representing the only connection. + *

+ *
Backpressure:
+ *
The operator itself doesn't interfere with backpressure which is determined by + * the upstream {@code ConnectableFlowable}'s behavior.
+ *
Scheduler:
+ *
{@code autoConnect} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableFlowable. A non-positive value indicates + * on the {@code ConnectableFlowable}. A non-positive value indicates * an immediate connection. - * @return a Flowable that automatically connects to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it + * @return a new {@code Flowable} instance that automatically connects to this {@code ConnectableFlowable} + * when the specified number of {@code Subscriber}s subscribe to it */ @NonNull + @CheckReturnValue + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public Flowable autoConnect(int numberOfSubscribers) { return autoConnect(numberOfSubscribers, Functions.emptyConsumer()); } /** - * Returns a Flowable that automatically connects (at most once) to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection. + * Returns a {@link Flowable} that automatically connects (at most once) to this {@code ConnectableFlowable} + * when the specified number of {@link Subscriber}s subscribe to it and calls the + * specified callback with the {@link Disposable} associated with the established connection. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Flowable. If this ConnectableFlowable - * terminates, the connection is never renewed, no matter how Subscribers come + * during the lifetime of the returned {@code Flowable}. If this {@code ConnectableFlowable} + * terminates, the connection is never renewed, no matter how {@code Subscriber}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Subscriber}s have cancelled their {@code Subscription}s. + * connection when all {@code Subscriber}s have cancelled their {@link Subscription}s. + *

+ *
Backpressure:
+ *
The operator itself doesn't interfere with backpressure which is determined by + * the upstream {@code ConnectableFlowable}'s behavior.
+ *
Scheduler:
+ *
{@code autoConnect} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableFlowable. A non-positive value indicates + * on the {@code ConnectableFlowable}. A non-positive value indicates * an immediate connection. - * @param connection the callback Consumer that will receive the Subscription representing the + * @param connection the callback {@link Consumer} that will receive the {@code Disposable} representing the * established connection - * @return a Flowable that automatically connects to this ConnectableFlowable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection + * @return a new {@code Flowable} instance that automatically connects to this {@code ConnectableFlowable} + * when the specified number of {@code Subscriber}s subscribe to it and calls the + * specified callback with the {@code Disposable} associated with the established connection + * @throws NullPointerException if {@code connection} is {@code null} */ @NonNull + @CheckReturnValue + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public Flowable autoConnect(int numberOfSubscribers, @NonNull Consumer connection) { + Objects.requireNonNull(connection, "connection is null"); if (numberOfSubscribers <= 0) { this.connect(connection); return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new FlowableAutoConnect(this, numberOfSubscribers, connection)); + return RxJavaPlugins.onAssembly(new FlowableAutoConnect<>(this, numberOfSubscribers, connection)); } } diff --git a/src/main/java/io/reactivex/rxjava3/flowables/GroupedFlowable.java b/src/main/java/io/reactivex/rxjava3/flowables/GroupedFlowable.java index d9564c3e6e..609fc7808f 100644 --- a/src/main/java/io/reactivex/rxjava3/flowables/GroupedFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/flowables/GroupedFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.flowables; import io.reactivex.rxjava3.annotations.Nullable; diff --git a/src/main/java/io/reactivex/rxjava3/flowables/package-info.java b/src/main/java/io/reactivex/rxjava3/flowables/package-info.java index 75a6dec4e9..06475a34ea 100644 --- a/src/main/java/io/reactivex/rxjava3/flowables/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/flowables/package-info.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ /** diff --git a/src/main/java/io/reactivex/rxjava3/functions/Action.java b/src/main/java/io/reactivex/rxjava3/functions/Action.java index 153eff9375..509e5fcb64 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Action.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Action.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,7 @@ /** * A functional interface similar to Runnable but allows throwing a checked exception. */ +@FunctionalInterface public interface Action { /** * Runs the action and optionally throws a checked exception. diff --git a/src/main/java/io/reactivex/rxjava3/functions/BiConsumer.java b/src/main/java/io/reactivex/rxjava3/functions/BiConsumer.java index c060c38331..09fec68cbf 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/BiConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/functions/BiConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,12 +13,15 @@ package io.reactivex.rxjava3.functions; +import io.reactivex.rxjava3.annotations.NonNull; + /** * A functional interface (callback) that accepts two values (of possibly different types). * @param the first value type * @param the second value type */ -public interface BiConsumer { +@FunctionalInterface +public interface BiConsumer<@NonNull T1, @NonNull T2> { /** * Performs an operation on the given values. diff --git a/src/main/java/io/reactivex/rxjava3/functions/BiFunction.java b/src/main/java/io/reactivex/rxjava3/functions/BiFunction.java index f7e1174c6e..27aa9ee016 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/BiFunction.java +++ b/src/main/java/io/reactivex/rxjava3/functions/BiFunction.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,8 @@ * @param the second value type * @param the result type */ -public interface BiFunction { +@FunctionalInterface +public interface BiFunction<@NonNull T1, @NonNull T2, @NonNull R> { /** * Calculate a value based on the input values. @@ -30,6 +31,5 @@ public interface BiFunction { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2) throws Throwable; + R apply(T1 t1, T2 t2) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/BiPredicate.java b/src/main/java/io/reactivex/rxjava3/functions/BiPredicate.java index 4793e1d086..e45397aab7 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/BiPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/functions/BiPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,7 +20,8 @@ * @param the first value * @param the second value */ -public interface BiPredicate { +@FunctionalInterface +public interface BiPredicate<@NonNull T1, @NonNull T2> { /** * Test the given input values and return a boolean. diff --git a/src/main/java/io/reactivex/rxjava3/functions/BooleanSupplier.java b/src/main/java/io/reactivex/rxjava3/functions/BooleanSupplier.java index dc801e5180..4e3b447b6e 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/BooleanSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/functions/BooleanSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,7 @@ /** * A functional interface (callback) that returns a boolean value. */ +@FunctionalInterface public interface BooleanSupplier { /** * Returns a boolean value. diff --git a/src/main/java/io/reactivex/rxjava3/functions/Cancellable.java b/src/main/java/io/reactivex/rxjava3/functions/Cancellable.java index 47683eafdc..574c832268 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Cancellable.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Cancellable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,6 +17,7 @@ * A functional interface that has a single cancel method * that can throw. */ +@FunctionalInterface public interface Cancellable { /** diff --git a/src/main/java/io/reactivex/rxjava3/functions/Consumer.java b/src/main/java/io/reactivex/rxjava3/functions/Consumer.java index 08d94eb928..3ac1806438 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Consumer.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Consumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,11 +13,14 @@ package io.reactivex.rxjava3.functions; +import io.reactivex.rxjava3.annotations.NonNull; + /** * A functional interface (callback) that accepts a single value. * @param the value type */ -public interface Consumer { +@FunctionalInterface +public interface Consumer<@NonNull T> { /** * Consume the given value. * @param t the value diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function.java b/src/main/java/io/reactivex/rxjava3/functions/Function.java index 23d06b041b..40e11ca3ba 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,12 +22,13 @@ * @param the input value type * @param the output value type */ -public interface Function { +@FunctionalInterface +public interface Function<@NonNull T, @NonNull R> { /** * Apply some calculation to the input value and return some other value. * @param t the input value * @return the output value * @throws Throwable if the implementation wishes to throw any type of exception */ - R apply(@NonNull T t) throws Throwable; + R apply(T t) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function3.java b/src/main/java/io/reactivex/rxjava3/functions/Function3.java index bdc1b4af2b..377cceaeaf 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function3.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function3.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,8 @@ * @param the third value type * @param the result type */ -public interface Function3 { +@FunctionalInterface +public interface Function3<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -31,6 +32,5 @@ public interface Function3 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function4.java b/src/main/java/io/reactivex/rxjava3/functions/Function4.java index 907f742907..36db385819 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function4.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function4.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,8 @@ * @param the fourth value type * @param the result type */ -public interface Function4 { +@FunctionalInterface +public interface Function4<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -33,6 +34,5 @@ public interface Function4 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3, @NonNull T4 t4) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3, T4 t4) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function5.java b/src/main/java/io/reactivex/rxjava3/functions/Function5.java index a8468844b7..d3fc1504c4 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function5.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function5.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,8 @@ * @param the fifth value type * @param the result type */ -public interface Function5 { +@FunctionalInterface +public interface Function5<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -35,6 +36,5 @@ public interface Function5 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3, @NonNull T4 t4, @NonNull T5 t5) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function6.java b/src/main/java/io/reactivex/rxjava3/functions/Function6.java index 6bc5caec1f..2969a39f82 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function6.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function6.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,8 @@ * @param the sixth value type * @param the result type */ -public interface Function6 { +@FunctionalInterface +public interface Function6<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -37,6 +38,5 @@ public interface Function6 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3, @NonNull T4 t4, @NonNull T5 t5, @NonNull T6 t6) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function7.java b/src/main/java/io/reactivex/rxjava3/functions/Function7.java index 32d5a08e41..091b203296 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function7.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function7.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,8 @@ * @param the seventh value type * @param the result type */ -public interface Function7 { +@FunctionalInterface +public interface Function7<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -39,6 +40,5 @@ public interface Function7 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3, @NonNull T4 t4, @NonNull T5 t5, @NonNull T6 t6, @NonNull T7 t7) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function8.java b/src/main/java/io/reactivex/rxjava3/functions/Function8.java index 8a7a88f9e8..c8e21acf9d 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function8.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function8.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,8 @@ * @param the eighth value type * @param the result type */ -public interface Function8 { +@FunctionalInterface +public interface Function8<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -41,6 +42,5 @@ public interface Function8 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3, @NonNull T4 t4, @NonNull T5 t5, @NonNull T6 t6, @NonNull T7 t7, @NonNull T8 t8) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Function9.java b/src/main/java/io/reactivex/rxjava3/functions/Function9.java index 28319eac14..0b182eb5f3 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Function9.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Function9.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,8 @@ * @param the ninth value type * @param the result type */ -public interface Function9 { +@FunctionalInterface +public interface Function9<@NonNull T1, @NonNull T2, @NonNull T3, @NonNull T4, @NonNull T5, @NonNull T6, @NonNull T7, @NonNull T8, @NonNull T9, @NonNull R> { /** * Calculate a value based on the input values. * @param t1 the first value @@ -43,6 +44,5 @@ public interface Function9 { * @return the result value * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull - R apply(@NonNull T1 t1, @NonNull T2 t2, @NonNull T3 t3, @NonNull T4 t4, @NonNull T5 t5, @NonNull T6 t6, @NonNull T7 t7, @NonNull T8 t8, @NonNull T9 t9) throws Throwable; + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/IntFunction.java b/src/main/java/io/reactivex/rxjava3/functions/IntFunction.java index 8a135ad491..3a405aac26 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/IntFunction.java +++ b/src/main/java/io/reactivex/rxjava3/functions/IntFunction.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.functions; import io.reactivex.rxjava3.annotations.NonNull; @@ -18,13 +19,13 @@ * A functional interface (callback) that takes a primitive value and return value of type T. * @param the returned value type */ -public interface IntFunction { +@FunctionalInterface +public interface IntFunction<@NonNull T> { /** * Calculates a value based on a primitive integer input. * @param i the input value * @return the result Object * @throws Throwable if the implementation wishes to throw any type of exception */ - @NonNull T apply(int i) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/LongConsumer.java b/src/main/java/io/reactivex/rxjava3/functions/LongConsumer.java index 6b5b72101c..5b12752c1d 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/LongConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/functions/LongConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,11 +10,13 @@ * 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 io.reactivex.rxjava3.functions; /** * A functional interface (callback) that consumes a primitive long value. */ +@FunctionalInterface public interface LongConsumer { /** * Consume a primitive long input. diff --git a/src/main/java/io/reactivex/rxjava3/functions/Predicate.java b/src/main/java/io/reactivex/rxjava3/functions/Predicate.java index 3197114158..1bce2d6bff 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Predicate.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Predicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,12 +19,13 @@ * A functional interface (callback) that returns true or false for the given input value. * @param the first value */ -public interface Predicate { +@FunctionalInterface +public interface Predicate<@NonNull T> { /** * Test the given input value and return a boolean. * @param t the value * @return the boolean result * @throws Throwable if the implementation wishes to throw any type of exception */ - boolean test(@NonNull T t) throws Throwable; + boolean test(T t) throws Throwable; } diff --git a/src/main/java/io/reactivex/rxjava3/functions/Supplier.java b/src/main/java/io/reactivex/rxjava3/functions/Supplier.java index 95ef2e88a1..ebf7fdda6d 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/Supplier.java +++ b/src/main/java/io/reactivex/rxjava3/functions/Supplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,6 +22,7 @@ * @param the value type returned * @since 3.0.0 */ +@FunctionalInterface public interface Supplier { /** diff --git a/src/main/java/io/reactivex/rxjava3/functions/package-info.java b/src/main/java/io/reactivex/rxjava3/functions/package-info.java index a7d92ce8fb..ab9dad0426 100644 --- a/src/main/java/io/reactivex/rxjava3/functions/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/functions/package-info.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ /** diff --git a/src/main/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposable.java index 5ebcdde15b..c9a6bc9bc4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposable.java index 314464e156..863f52e91b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/disposables/DisposableHelper.java b/src/main/java/io/reactivex/rxjava3/internal/disposables/DisposableHelper.java index 3a8c5d7029..79dbd855ac 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/disposables/DisposableHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/disposables/DisposableHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,11 +13,11 @@ package io.reactivex.rxjava3.internal.disposables; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.ProtocolViolationException; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -75,7 +75,7 @@ public static boolean set(AtomicReference field, Disposable d) { * @return true if the operation succeeded, false */ public static boolean setOnce(AtomicReference field, Disposable d) { - ObjectHelper.requireNonNull(d, "d is null"); + Objects.requireNonNull(d, "d is null"); if (!field.compareAndSet(null, d)) { d.dispose(); if (field.get() != DISPOSED) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposable.java index d08ce8d14a..fdb30932ed 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; /** * Represents a stateless empty Disposable that reports being always @@ -95,7 +95,7 @@ public boolean offer(Object v1, Object v2) { @Nullable @Override - public Object poll() throws Exception { + public Object poll() { return null; // always empty } diff --git a/src/main/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposable.java index f3afcbf2c0..4c4b1f8a73 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,13 +10,13 @@ * 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 io.reactivex.rxjava3.internal.disposables; import java.util.*; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.ExceptionHelper; /** @@ -32,19 +32,19 @@ public ListCompositeDisposable() { } public ListCompositeDisposable(Disposable... resources) { - ObjectHelper.requireNonNull(resources, "resources is null"); - this.resources = new LinkedList(); + Objects.requireNonNull(resources, "resources is null"); + this.resources = new LinkedList<>(); for (Disposable d : resources) { - ObjectHelper.requireNonNull(d, "Disposable item is null"); + Objects.requireNonNull(d, "Disposable item is null"); this.resources.add(d); } } public ListCompositeDisposable(Iterable resources) { - ObjectHelper.requireNonNull(resources, "resources is null"); - this.resources = new LinkedList(); + Objects.requireNonNull(resources, "resources is null"); + this.resources = new LinkedList<>(); for (Disposable d : resources) { - ObjectHelper.requireNonNull(d, "Disposable item is null"); + Objects.requireNonNull(d, "Disposable item is null"); this.resources.add(d); } } @@ -74,13 +74,13 @@ public boolean isDisposed() { @Override public boolean add(Disposable d) { - ObjectHelper.requireNonNull(d, "d is null"); + Objects.requireNonNull(d, "d is null"); if (!disposed) { synchronized (this) { if (!disposed) { List set = resources; if (set == null) { - set = new LinkedList(); + set = new LinkedList<>(); resources = set; } set.add(d); @@ -93,17 +93,17 @@ public boolean add(Disposable d) { } public boolean addAll(Disposable... ds) { - ObjectHelper.requireNonNull(ds, "ds is null"); + Objects.requireNonNull(ds, "ds is null"); if (!disposed) { synchronized (this) { if (!disposed) { List set = resources; if (set == null) { - set = new LinkedList(); + set = new LinkedList<>(); resources = set; } for (Disposable d : ds) { - ObjectHelper.requireNonNull(d, "d is null"); + Objects.requireNonNull(d, "d is null"); set.add(d); } return true; @@ -127,7 +127,7 @@ public boolean remove(Disposable d) { @Override public boolean delete(Disposable d) { - ObjectHelper.requireNonNull(d, "Disposable item is null"); + Objects.requireNonNull(d, "Disposable item is null"); if (disposed) { return false; } @@ -172,7 +172,7 @@ void dispose(List set) { } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (errors == null) { - errors = new ArrayList(); + errors = new ArrayList<>(); } errors.add(ex); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/disposables/SequentialDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/disposables/SequentialDisposable.java index d9922ebec8..f6aef6bdd3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/disposables/SequentialDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/disposables/SequentialDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java b/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java index 87aae2e1e8..e0d3dead3c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java +++ b/src/main/java/io/reactivex/rxjava3/internal/functions/Functions.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.functions; import java.util.*; @@ -17,6 +18,7 @@ import org.reactivestreams.Subscription; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException; import io.reactivex.rxjava3.functions.*; @@ -33,48 +35,48 @@ private Functions() { throw new IllegalStateException("No instances!"); } - public static Function toFunction(final BiFunction f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array2Func(f); + @NonNull + public static Function toFunction(@NonNull BiFunction f) { + return new Array2Func<>(f); } - public static Function toFunction(final Function3 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array3Func(f); + @NonNull + public static Function toFunction(@NonNull Function3 f) { + return new Array3Func<>(f); } - public static Function toFunction(final Function4 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array4Func(f); + @NonNull + public static Function toFunction(@NonNull Function4 f) { + return new Array4Func<>(f); } - public static Function toFunction(final Function5 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array5Func(f); + @NonNull + public static Function toFunction(@NonNull Function5 f) { + return new Array5Func<>(f); } + @NonNull public static Function toFunction( - final Function6 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array6Func(f); + @NonNull Function6 f) { + return new Array6Func<>(f); } + @NonNull public static Function toFunction( - final Function7 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array7Func(f); + @NonNull Function7 f) { + return new Array7Func<>(f); } + @NonNull public static Function toFunction( - final Function8 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array8Func(f); + @NonNull Function8 f) { + return new Array8Func<>(f); } + @NonNull public static Function toFunction( - final Function9 f) { - ObjectHelper.requireNonNull(f, "f is null"); - return new Array9Func(f); + @NonNull Function9 f) { + return new Array9Func<>(f); } /** A singleton identity function. */ @@ -86,6 +88,7 @@ public static Function toFu * @return the identity function */ @SuppressWarnings("unchecked") + @NonNull public static Function identity() { return (Function)IDENTITY; } @@ -122,33 +125,24 @@ public static Consumer emptyConsumer() { static final Supplier NULL_SUPPLIER = new NullProvider(); - static final Comparator NATURAL_COMPARATOR = new NaturalObjectComparator(); - @SuppressWarnings("unchecked") + @NonNull public static Predicate alwaysTrue() { return (Predicate)ALWAYS_TRUE; } @SuppressWarnings("unchecked") + @NonNull public static Predicate alwaysFalse() { return (Predicate)ALWAYS_FALSE; } @SuppressWarnings("unchecked") + @NonNull public static Supplier nullSupplier() { return (Supplier)NULL_SUPPLIER; } - /** - * Returns a natural order comparator which casts the parameters to Comparable. - * @param the value type - * @return a natural order comparator which casts the parameters to Comparable - */ - @SuppressWarnings("unchecked") - public static Comparator naturalOrder() { - return (Comparator)NATURAL_COMPARATOR; - } - static final class FutureAction implements Action { final Future future; @@ -167,7 +161,8 @@ public void run() throws Exception { * @param future the future to call get() on, not null * @return the new Action instance */ - public static Action futureAction(Future future) { + @NonNull + public static Action futureAction(@NonNull Future future) { return new FutureAction(future); } @@ -179,17 +174,17 @@ static final class JustValue implements Callable, Supplier, Function } @Override - public U call() throws Exception { + public U call() { return value; } @Override - public U apply(T t) throws Exception { + public U apply(T t) { return value; } @Override - public U get() throws Throwable { + public U get() { return value; } } @@ -200,8 +195,9 @@ public U get() throws Throwable { * @param value the value to return * @return the new Callable instance */ - public static Callable justCallable(T value) { - return new JustValue(value); + @NonNull + public static Callable justCallable(@NonNull T value) { + return new JustValue<>(value); } /** @@ -210,8 +206,9 @@ public static Callable justCallable(T value) { * @param value the value to return * @return the new Callable instance */ - public static Supplier justSupplier(T value) { - return new JustValue(value); + @NonNull + public static Supplier justSupplier(@NonNull T value) { + return new JustValue<>(value); } /** @@ -221,8 +218,9 @@ public static Supplier justSupplier(T value) { * @param value the value to return * @return the new Function instance */ - public static Function justFunction(U value) { - return new JustValue(value); + @NonNull + public static Function justFunction(@NonNull U value) { + return new JustValue<>(value); } static final class CastToClass implements Function { @@ -233,7 +231,7 @@ static final class CastToClass implements Function { } @Override - public U apply(T t) throws Exception { + public U apply(T t) { return clazz.cast(t); } } @@ -245,8 +243,9 @@ public U apply(T t) throws Exception { * @param target the target class * @return the new Function instance */ - public static Function castFunction(Class target) { - return new CastToClass(target); + @NonNull + public static Function castFunction(@NonNull Class target) { + return new CastToClass<>(target); } static final class ArrayListCapacityCallable implements Supplier> { @@ -257,13 +256,13 @@ static final class ArrayListCapacityCallable implements Supplier> { } @Override - public List get() throws Exception { - return new ArrayList(capacity); + public List get() { + return new ArrayList<>(capacity); } } public static Supplier> createArrayList(int capacity) { - return new ArrayListCapacityCallable(capacity); + return new ArrayListCapacityCallable<>(capacity); } static final class EqualsPredicate implements Predicate { @@ -274,31 +273,26 @@ static final class EqualsPredicate implements Predicate { } @Override - public boolean test(T t) throws Exception { - return ObjectHelper.equals(t, value); + public boolean test(T t) { + return Objects.equals(t, value); } } public static Predicate equalsWith(T value) { - return new EqualsPredicate(value); + return new EqualsPredicate<>(value); } - enum HashSetCallable implements Supplier>, Callable> { + enum HashSetSupplier implements Supplier> { INSTANCE; @Override - public Set call() throws Exception { - return new HashSet(); - } - - @Override - public Set get() throws Throwable { - return new HashSet(); + public Set get() { + return new HashSet<>(); } } @SuppressWarnings({ "rawtypes", "unchecked" }) public static Supplier> createHashSet() { - return (Supplier)HashSetCallable.INSTANCE; + return (Supplier)HashSetSupplier.INSTANCE; } static final class NotificationOnNext implements Consumer { @@ -323,7 +317,7 @@ static final class NotificationOnError implements Consumer { @Override public void accept(Throwable v) throws Throwable { - onNotification.accept(Notification.createOnError(v)); + onNotification.accept(Notification.createOnError(v)); } } @@ -336,20 +330,20 @@ static final class NotificationOnComplete implements Action { @Override public void run() throws Throwable { - onNotification.accept(Notification.createOnComplete()); + onNotification.accept(Notification.createOnComplete()); } } public static Consumer notificationOnNext(Consumer> onNotification) { - return new NotificationOnNext(onNotification); + return new NotificationOnNext<>(onNotification); } public static Consumer notificationOnError(Consumer> onNotification) { - return new NotificationOnError(onNotification); + return new NotificationOnError<>(onNotification); } public static Action notificationOnComplete(Consumer> onNotification) { - return new NotificationOnComplete(onNotification); + return new NotificationOnComplete<>(onNotification); } static final class ActionConsumer implements Consumer { @@ -366,7 +360,7 @@ public void accept(T t) throws Throwable { } public static Consumer actionConsumer(Action action) { - return new ActionConsumer(action); + return new ActionConsumer<>(action); } static final class ClassFilter implements Predicate { @@ -377,13 +371,13 @@ static final class ClassFilter implements Predicate { } @Override - public boolean test(T t) throws Exception { + public boolean test(T t) { return clazz.isInstance(t); } } public static Predicate isInstanceOf(Class clazz) { - return new ClassFilter(clazz); + return new ClassFilter<>(clazz); } static final class BooleanSupplierPredicateReverse implements Predicate { @@ -400,7 +394,7 @@ public boolean test(T t) throws Throwable { } public static Predicate predicateReverseFor(BooleanSupplier supplier) { - return new BooleanSupplierPredicateReverse(supplier); + return new BooleanSupplierPredicateReverse<>(supplier); } static final class TimestampFunction implements Function> { @@ -414,13 +408,13 @@ static final class TimestampFunction implements Function> { } @Override - public Timed apply(T t) throws Exception { - return new Timed(t, scheduler.now(unit), unit); + public Timed apply(T t) { + return new Timed<>(t, scheduler.now(unit), unit); } } public static Function> timestampWith(TimeUnit unit, Scheduler scheduler) { - return new TimestampFunction(unit, scheduler); + return new TimestampFunction<>(unit, scheduler); } static final class ToMapKeySelector implements BiConsumer, T> { @@ -438,7 +432,7 @@ public void accept(Map m, T t) throws Throwable { } public static BiConsumer, T> toMapKeySelector(final Function keySelector) { - return new ToMapKeySelector(keySelector); + return new ToMapKeySelector<>(keySelector); } static final class ToMapKeyValueSelector implements BiConsumer, T> { @@ -460,7 +454,7 @@ public void accept(Map m, T t) throws Throwable { } public static BiConsumer, T> toMapKeyValueSelector(final Function keySelector, final Function valueSelector) { - return new ToMapKeyValueSelector(valueSelector, keySelector); + return new ToMapKeyValueSelector<>(valueSelector, keySelector); } static final class ToMultimapKeyValueSelector implements BiConsumer>, T> { @@ -495,7 +489,7 @@ public void accept(Map> m, T t) throws Throwable { public static BiConsumer>, T> toMultimapKeyValueSelector( final Function keySelector, final Function valueSelector, final Function> collectionFactory) { - return new ToMultimapKeyValueSelector(collectionFactory, valueSelector, keySelector); + return new ToMultimapKeyValueSelector<>(collectionFactory, valueSelector, keySelector); } enum NaturalComparator implements Comparator { @@ -528,7 +522,7 @@ public List apply(List v) { } public static Function, List> listSorter(final Comparator comparator) { - return new ListSorter(comparator); + return new ListSorter<>(comparator); } public static final Consumer REQUEST_MAX = new MaxRequestSubscription(); @@ -744,29 +738,16 @@ public boolean test(Object o) { } } - static final class NullProvider implements Callable, Supplier { - @Override - public Object call() { - return null; - } - + static final class NullProvider implements Supplier { @Override - public Object get() throws Throwable { + public Object get() { return null; } } - static final class NaturalObjectComparator implements Comparator { - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public int compare(Object a, Object b) { - return ((Comparable)a).compareTo(b); - } - } - static final class MaxRequestSubscription implements Consumer { @Override - public void accept(Subscription t) throws Exception { + public void accept(Subscription t) { t.request(Long.MAX_VALUE); } } @@ -785,7 +766,7 @@ public static class BoundedConsumer implements Consumer { } @Override - public void accept(Subscription s) throws Exception { + public void accept(Subscription s) { s.request(bufferSize); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/functions/ObjectHelper.java b/src/main/java/io/reactivex/rxjava3/internal/functions/ObjectHelper.java index c13b1850f2..5bbdf22ad3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/functions/ObjectHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/functions/ObjectHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,9 +10,11 @@ * 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 io.reactivex.rxjava3.internal.functions; import io.reactivex.rxjava3.functions.BiPredicate; +import java.util.Objects; /** * Utility methods containing the backport of Java 7's Objects utility class. @@ -25,61 +27,6 @@ private ObjectHelper() { throw new IllegalStateException("No instances!"); } - /** - * Verifies if the object is not null and returns it or throws a NullPointerException - * with the given message. - * @param the value type - * @param object the object to verify - * @param message the message to use with the NullPointerException - * @return the object itself - * @throws NullPointerException if object is null - */ - public static T requireNonNull(T object, String message) { - if (object == null) { - throw new NullPointerException(message); - } - return object; - } - - /** - * Compares two potentially null objects with each other using Object.equals. - * @param o1 the first object - * @param o2 the second object - * @return the comparison result - */ - public static boolean equals(Object o1, Object o2) { // NOPMD - return o1 == o2 || (o1 != null && o1.equals(o2)); - } - - /** - * Returns the hashCode of a non-null object or zero for a null object. - * @param o the object to get the hashCode for. - * @return the hashCode - */ - public static int hashCode(Object o) { - return o != null ? o.hashCode() : 0; - } - - /** - * Compares two integer values similar to Integer.compare. - * @param v1 the first value - * @param v2 the second value - * @return the comparison result - */ - public static int compare(int v1, int v2) { - return v1 < v2 ? -1 : (v1 > v2 ? 1 : 0); - } - - /** - * Compares two long values similar to Long.compare. - * @param v1 the first value - * @param v2 the second value - * @return the comparison result - */ - public static int compare(long v1, long v2) { - return v1 < v2 ? -1 : (v1 > v2 ? 1 : 0); - } - static final BiPredicate EQUALS = new BiObjectPredicate(); /** @@ -125,20 +72,7 @@ public static long verifyPositive(long value, String paramName) { static final class BiObjectPredicate implements BiPredicate { @Override public boolean test(Object o1, Object o2) { - return ObjectHelper.equals(o1, o2); + return Objects.equals(o1, o2); } } - - /** - * Trap null-check attempts on primitives. - * @param value the value to check - * @param message the message to print - * @return the value - * @deprecated this method should not be used as there is no need - * to check primitives for nullness. - */ - @Deprecated - public static long requireNonNull(long value, String message) { - throw new InternalError("Null check on a primitive: " + message); - } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/AbstractEmptyQueueFuseable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/AbstractEmptyQueueFuseable.java new file mode 100644 index 0000000000..13732bd426 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/AbstractEmptyQueueFuseable.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.fuseable; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; + +/** + * Represents an empty, async-only {@link QueueFuseable} instance. + * + * @param the output value type + * @since 3.0.0 + */ +public abstract class AbstractEmptyQueueFuseable +implements QueueSubscription, QueueDisposable { + + @Override + public final int requestFusion(int mode) { + return mode & ASYNC; + } + + @Override + public final boolean offer(@NonNull T value) { + throw new UnsupportedOperationException("Should not be called!"); + } + + @Override + public final boolean offer(@NonNull T v1, @NonNull T v2) { + throw new UnsupportedOperationException("Should not be called!"); + } + + @Override + public final T poll() throws Throwable { + return null; // always empty + } + + @Override + public final boolean isEmpty() { + return true; // always empty + } + + @Override + public final void clear() { + // always empty + } + + @Override + public final void request(long n) { + // no items to request + } + + @Override + public void cancel() { + // default No-op + } + + @Override + public void dispose() { + // default No-op + } + + @Override + public boolean isDisposed() { + return false; + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseable.java new file mode 100644 index 0000000000..71f993e226 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseable.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.fuseable; + +import io.reactivex.rxjava3.operators.QueueFuseable; + +/** + * Represents an empty, async-only {@link QueueFuseable} instance that tracks and exposes a + * canceled/disposed state. + * + * @param the output value type + * @since 3.0.0 + */ +public final class CancellableQueueFuseable +extends AbstractEmptyQueueFuseable { + + volatile boolean disposed; + + @Override + public void cancel() { + disposed = true; + } + + @Override + public void dispose() { + disposed = true; + } + + @Override + public boolean isDisposed() { + return disposed; + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java index 2b7bb614e7..fe70b40a01 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Flowable; /** @@ -20,8 +21,10 @@ * the operator goes from Flowable to some other reactive type and then the sequence calls * for toFlowable again: *
- * Single<Integer> single = Flowable.range(1, 10).reduce((a, b) -> a + b);
- * Flowable<Integer> flowable = single.toFlowable();
+ * {@code
+ * Single single = Flowable.range(1, 10).reduce((a, b) -> a + b);
+ * Flowable flowable = single.toFlowable();
+ * }
  * 
* * The {@code Single.toFlowable()} will check for this interface and call the {@link #fuseToFlowable()} @@ -32,12 +35,13 @@ * * @param the value type */ -public interface FuseToFlowable { +public interface FuseToFlowable<@NonNull T> { /** * Returns a (direct) Flowable for the operator. *

The implementation should handle the necessary RxJavaPlugins wrapping. * @return the Flowable instance */ + @NonNull Flowable fuseToFlowable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java index c2db8c7de6..13ccd6be75 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Maybe; /** @@ -20,8 +21,10 @@ * the operator goes from Maybe to some other reactive type and then the sequence calls * for toMaybe again: *

- * Single<Integer> single = Maybe.just(1).isEmpty();
- * Maybe<Integer> maybe = single.toMaybe();
+ * {@code
+ * Single single = Maybe.just(1).isEmpty();
+ * Maybe maybe = single.toMaybe();
+ * }
  * 
* * The {@code Single.toMaybe()} will check for this interface and call the {@link #fuseToMaybe()} @@ -32,12 +35,13 @@ * * @param the value type */ -public interface FuseToMaybe { +public interface FuseToMaybe<@NonNull T> { /** * Returns a (direct) Maybe for the operator. *

The implementation should handle the necessary RxJavaPlugins wrapping. * @return the Maybe instance */ + @NonNull Maybe fuseToMaybe(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java index 8488ccf6e8..d7ae761e37 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/FuseToObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observable; /** @@ -20,8 +21,10 @@ * the operator goes from Observable to some other reactive type and then the sequence calls * for toObservable again: *

- * Single<Integer> single = Observable.range(1, 10).reduce((a, b) -> a + b);
- * Observable<Integer> observable = single.toObservable();
+ * {@code
+ * Single single = Observable.range(1, 10).reduce((a, b) -> a + b);
+ * Observable observable = single.toObservable();
+ * }
  * 
* * The {@code Single.toObservable()} will check for this interface and call the {@link #fuseToObservable()} @@ -32,12 +35,13 @@ * * @param the value type */ -public interface FuseToObservable { +public interface FuseToObservable<@NonNull T> { /** * Returns a (direct) Observable for the operator. *

The implementation should handle the necessary RxJavaPlugins wrapping. * @return the Observable instance */ + @NonNull Observable fuseToObservable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamCompletableSource.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamCompletableSource.java index 38f199b9dc..fddb14ef0b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamCompletableSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamCompletableSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.CompletableSource; /** @@ -25,5 +26,6 @@ public interface HasUpstreamCompletableSource { *

Allows discovering the chain of observables. * @return the source CompletableSource */ + @NonNull CompletableSource source(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamMaybeSource.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamMaybeSource.java index 0a7d36ed86..121e2937e4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamMaybeSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamMaybeSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.MaybeSource; /** @@ -21,11 +22,12 @@ * * @param the value type */ -public interface HasUpstreamMaybeSource { +public interface HasUpstreamMaybeSource<@NonNull T> { /** * Returns the upstream source of this Maybe. *

Allows discovering the chain of observables. * @return the source MaybeSource */ + @NonNull MaybeSource source(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamObservableSource.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamObservableSource.java index 0ab946c4c0..1a1e878b65 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamObservableSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamObservableSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.ObservableSource; /** @@ -21,11 +22,12 @@ * * @param the value type */ -public interface HasUpstreamObservableSource { +public interface HasUpstreamObservableSource<@NonNull T> { /** * Returns the upstream source of this Observable. *

Allows discovering the chain of observables. * @return the source ObservableSource */ + @NonNull ObservableSource source(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamPublisher.java index 9afa5ce9e0..5a46f38555 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,13 +15,15 @@ import org.reactivestreams.Publisher; +import io.reactivex.rxjava3.annotations.NonNull; + /** * Interface indicating the implementor has an upstream Publisher-like source available * via {@link #source()} method. * * @param the value type */ -public interface HasUpstreamPublisher { +public interface HasUpstreamPublisher<@NonNull T> { /** * Returns the source Publisher. *

@@ -29,5 +31,6 @@ public interface HasUpstreamPublisher { * graph of sequences. * @return the source Publisher */ + @NonNull Publisher source(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamSingleSource.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamSingleSource.java index db94a572e1..ef833ae2ff 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamSingleSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/HasUpstreamSingleSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.fuseable; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.SingleSource; /** @@ -21,11 +22,12 @@ * * @param the value type */ -public interface HasUpstreamSingleSource { +public interface HasUpstreamSingleSource<@NonNull T> { /** * Returns the upstream source of this Single. *

Allows discovering the chain of observables. * @return the source SingleSource */ + @NonNull SingleSource source(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/package-info.java b/src/main/java/io/reactivex/rxjava3/internal/fuseable/package-info.java index b4ee869076..339eabbfd2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/internal/fuseable/package-info.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/CompletableFromCompletionStage.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/CompletableFromCompletionStage.java new file mode 100644 index 0000000000..639516d005 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/CompletableFromCompletionStage.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.jdk8.FlowableFromCompletionStage.BiConsumerAtomicReference; + +/** + * Wrap a CompletionStage and signal its outcome. + * @param the element type of the CompletionsStage + * @since 3.0.0 + */ +public final class CompletableFromCompletionStage extends Completable { + + final CompletionStage stage; + + public CompletableFromCompletionStage(CompletionStage stage) { + this.stage = stage; + } + + @Override + protected void subscribeActual(CompletableObserver observer) { + // We need an indirection because one can't detach from a whenComplete + // and cancellation should not hold onto the stage. + BiConsumerAtomicReference whenReference = new BiConsumerAtomicReference<>(); + CompletionStageHandler handler = new CompletionStageHandler<>(observer, whenReference); + whenReference.lazySet(handler); + + observer.onSubscribe(handler); + stage.whenComplete(whenReference); + } + + static final class CompletionStageHandler + implements Disposable, BiConsumer { + + final CompletableObserver downstream; + + final BiConsumerAtomicReference whenReference; + + CompletionStageHandler(CompletableObserver downstream, BiConsumerAtomicReference whenReference) { + this.downstream = downstream; + this.whenReference = whenReference; + } + + @Override + public void accept(T item, Throwable error) { + if (error != null) { + downstream.onError(error); + } else { + downstream.onComplete(); + } + } + + @Override + public void dispose() { + whenReference.set(null); + } + + @Override + public boolean isDisposed() { + return whenReference.get() == null; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/CompletionStageConsumer.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/CompletionStageConsumer.java new file mode 100644 index 0000000000..2410290734 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/CompletionStageConsumer.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Class that extends CompletableFuture and converts multiple types of reactive consumers + * and their signals into completion signals. + * @param the element type + * @since 3.0.0 + */ +public final class CompletionStageConsumer extends CompletableFuture +implements MaybeObserver, SingleObserver, CompletableObserver { + + final AtomicReference upstream; + + final boolean hasDefault; + + final T defaultItem; + + public CompletionStageConsumer(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + this.upstream = new AtomicReference<>(); + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + DisposableHelper.setOnce(upstream, d); + } + + @Override + public void onSuccess(@NonNull T t) { + clear(); + complete(t); + } + + @Override + public void onError(Throwable t) { + clear(); + if (!completeExceptionally(t)) { + RxJavaPlugins.onError(t); + } + } + + @Override + public void onComplete() { + if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException("The source was empty")); + } + } + + void cancelUpstream() { + DisposableHelper.dispose(upstream); + } + + void clear() { + upstream.lazySet(DisposableHelper.DISPOSED); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + cancelUpstream(); + return super.cancel(mayInterruptIfRunning); + } + + @Override + public boolean complete(T value) { + cancelUpstream(); + return super.complete(value); + } + + @Override + public boolean completeExceptionally(Throwable ex) { + cancelUpstream(); + return super.completeExceptionally(ex); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java new file mode 100644 index 0000000000..63ca949295 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollector.java @@ -0,0 +1,154 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Objects; +import java.util.function.*; +import java.util.stream.Collector; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Collect items into a container defined by a Stream {@link Collector} callback set. + * + * @param the upstream value type + * @param the intermediate accumulator type + * @param the result type + * @since 3.0.0 + */ +public final class FlowableCollectWithCollector extends Flowable { + + final Flowable source; + + final Collector collector; + + public FlowableCollectWithCollector(Flowable source, Collector collector) { + this.source = source; + this.collector = collector; + } + + @Override + protected void subscribeActual(@NonNull Subscriber s) { + A container; + BiConsumer accumulator; + Function finisher; + + try { + container = collector.supplier().get(); + accumulator = collector.accumulator(); + finisher = collector.finisher(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + return; + } + + source.subscribe(new CollectorSubscriber<>(s, container, accumulator, finisher)); + } + + static final class CollectorSubscriber + extends DeferredScalarSubscription + implements FlowableSubscriber { + + private static final long serialVersionUID = -229544830565448758L; + + final BiConsumer accumulator; + + final Function finisher; + + Subscription upstream; + + boolean done; + + A container; + + CollectorSubscriber(Subscriber downstream, A container, BiConsumer accumulator, Function finisher) { + super(downstream); + this.container = container; + this.accumulator = accumulator; + this.finisher = finisher; + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + + s.request(Long.MAX_VALUE); + } + } + + @Override + public void onNext(T t) { + if (done) { + return; + } + try { + accumulator.accept(container, t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + onError(ex); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + } else { + done = true; + upstream = SubscriptionHelper.CANCELLED; + this.container = null; + downstream.onError(t); + } + } + + @Override + public void onComplete() { + if (done) { + return; + } + + done = true; + upstream = SubscriptionHelper.CANCELLED; + A container = this.container; + this.container = null; + R result; + try { + result = Objects.requireNonNull(finisher.apply(container), "The finisher returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + complete(result); + } + + @Override + public void cancel() { + super.cancel(); + upstream.cancel(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java new file mode 100644 index 0000000000..aba390e1da --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorSingle.java @@ -0,0 +1,165 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Objects; +import java.util.function.*; +import java.util.stream.Collector; + +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.internal.fuseable.FuseToFlowable; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Collect items into a container defined by a Stream {@link Collector} callback set. + * + * @param the upstream value type + * @param the intermediate accumulator type + * @param the result type + * @since 3.0.0 + */ +public final class FlowableCollectWithCollectorSingle extends Single implements FuseToFlowable { + + final Flowable source; + + final Collector collector; + + public FlowableCollectWithCollectorSingle(Flowable source, Collector collector) { + this.source = source; + this.collector = collector; + } + + @Override + public Flowable fuseToFlowable() { + return new FlowableCollectWithCollector<>(source, collector); + } + + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + A container; + BiConsumer accumulator; + Function finisher; + + try { + container = collector.supplier().get(); + accumulator = collector.accumulator(); + finisher = collector.finisher(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + return; + } + + source.subscribe(new CollectorSingleObserver<>(observer, container, accumulator, finisher)); + } + + static final class CollectorSingleObserver implements FlowableSubscriber, Disposable { + + final SingleObserver downstream; + + final BiConsumer accumulator; + + final Function finisher; + + Subscription upstream; + + boolean done; + + A container; + + CollectorSingleObserver(SingleObserver downstream, A container, BiConsumer accumulator, Function finisher) { + this.downstream = downstream; + this.container = container; + this.accumulator = accumulator; + this.finisher = finisher; + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + + s.request(Long.MAX_VALUE); + } + } + + @Override + public void onNext(T t) { + if (done) { + return; + } + try { + accumulator.accept(container, t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + onError(ex); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + } else { + done = true; + upstream = SubscriptionHelper.CANCELLED; + this.container = null; + downstream.onError(t); + } + } + + @Override + public void onComplete() { + if (done) { + return; + } + + done = true; + upstream = SubscriptionHelper.CANCELLED; + A container = this.container; + this.container = null; + R result; + try { + result = Objects.requireNonNull(finisher.apply(container), "The finisher returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + downstream.onSuccess(result); + } + + @Override + public void dispose() { + upstream.cancel(); + upstream = SubscriptionHelper.CANCELLED; + } + + @Override + public boolean isDisposed() { + return upstream == SubscriptionHelper.CANCELLED; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFirstStageSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFirstStageSubscriber.java new file mode 100644 index 0000000000..19bfcbff6d --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFirstStageSubscriber.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; + +import org.reactivestreams.Subscription; + +/** + * Signals the first element of the source via the underlying CompletableFuture, + * signals the a default item if the upstream is empty or signals {@link NoSuchElementException}. + * + * @param the element type + * @since 3.0.0 + */ +public final class FlowableFirstStageSubscriber extends FlowableStageSubscriber { + + final boolean hasDefault; + + final T defaultItem; + + public FlowableFirstStageSubscriber(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + } + + @Override + public void onNext(T t) { + complete(t); + } + + @Override + public void onComplete() { + if (!isDone()) { + clear(); + if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException()); + } + } + } + + @Override + protected void afterSubscribe(Subscription s) { + s.request(1); + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFlatMapStream.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFlatMapStream.java new file mode 100644 index 0000000000..48ea8b6e5f --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFlatMapStream.java @@ -0,0 +1,353 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.stream.Stream; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Maps the upstream values onto {@link Stream}s and emits their items in order to the downstream. + * + * @param the upstream element type + * @param the inner {@code Stream} and result element type + * @since 3.0.0 + */ +public final class FlowableFlatMapStream extends Flowable { + + final Flowable source; + + final Function> mapper; + + final int prefetch; + + public FlowableFlatMapStream(Flowable source, Function> mapper, int prefetch) { + this.source = source; + this.mapper = mapper; + this.prefetch = prefetch; + } + + @Override + protected void subscribeActual(Subscriber s) { + if (source instanceof Supplier) { + Stream stream = null; + try { + @SuppressWarnings("unchecked") + T t = ((Supplier)source).get(); + if (t != null) { + stream = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Stream"); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + return; + } + + if (stream != null) { + FlowableFromStream.subscribeStream(s, stream); + } else { + EmptySubscription.complete(s); + } + } else { + source.subscribe(subscribe(s, mapper, prefetch)); + } + } + + /** + * Create a {@link Subscriber} with the given parameters. + * @param the upstream value type + * @param the {@link Stream} and output value type + * @param downstream the downstream {@code Subscriber} to wrap + * @param mapper the mapper function + * @param prefetch the number of items to prefetch + * @return the new {@code Subscriber} + */ + public static Subscriber subscribe(Subscriber downstream, Function> mapper, int prefetch) { + return new FlatMapStreamSubscriber<>(downstream, mapper, prefetch); + } + + static final class FlatMapStreamSubscriber extends AtomicInteger + implements FlowableSubscriber, Subscription { + + private static final long serialVersionUID = -5127032662980523968L; + + final Subscriber downstream; + + final Function> mapper; + + final int prefetch; + + final AtomicLong requested; + + SimpleQueue queue; + + Subscription upstream; + + Iterator currentIterator; + + AutoCloseable currentCloseable; + + volatile boolean cancelled; + + volatile boolean upstreamDone; + final AtomicThrowable error; + + long emitted; + + int consumed; + + int sourceMode; + + FlatMapStreamSubscriber(Subscriber downstream, Function> mapper, int prefetch) { + this.downstream = downstream; + this.mapper = mapper; + this.prefetch = prefetch; + this.requested = new AtomicLong(); + this.error = new AtomicThrowable(); + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + if (s instanceof QueueSubscription) { + + @SuppressWarnings("unchecked") + QueueSubscription qs = (QueueSubscription)s; + + int m = qs.requestFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + if (m == QueueFuseable.SYNC) { + sourceMode = m; + queue = qs; + upstreamDone = true; + + downstream.onSubscribe(this); + return; + } + else if (m == QueueFuseable.ASYNC) { + sourceMode = m; + queue = qs; + + downstream.onSubscribe(this); + + s.request(prefetch); + return; + } + } + + queue = new SpscArrayQueue<>(prefetch); + + downstream.onSubscribe(this); + + s.request(prefetch); + } + } + + @Override + public void onNext(T t) { + if (sourceMode != QueueFuseable.ASYNC) { + if (!queue.offer(t)) { + upstream.cancel(); + onError(new QueueOverflowException()); + return; + } + } + drain(); + } + + @Override + public void onError(Throwable t) { + if (error.compareAndSet(null, t)) { + upstreamDone = true; + drain(); + } else { + RxJavaPlugins.onError(t); + } + } + + @Override + public void onComplete() { + upstreamDone = true; + drain(); + } + + @Override + public void request(long n) { + if (SubscriptionHelper.validate(n)) { + BackpressureHelper.add(requested, n); + drain(); + } + } + + @Override + public void cancel() { + cancelled = true; + upstream.cancel(); + drain(); + } + + void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + + final Subscriber downstream = this.downstream; + final SimpleQueue queue = this.queue; + final AtomicThrowable error = this.error; + Iterator iterator = this.currentIterator; + long requested = this.requested.get(); + long emitted = this.emitted; + final int limit = prefetch - (prefetch >> 2); + boolean canRequest = sourceMode != QueueFuseable.SYNC; + + for (;;) { + if (cancelled) { + queue.clear(); + clearCurrentSuppressCloseError(); + } else { + boolean isDone = upstreamDone; + if (error.get() != null) { + downstream.onError(error.get()); + cancelled = true; + continue; + } + + if (iterator == null) { + T t; + + try { + t = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + trySignalError(downstream, ex); + continue; + } + + boolean isEmpty = t == null; + + if (isDone && isEmpty) { + downstream.onComplete(); + cancelled = true; + } + else if (!isEmpty) { + if (canRequest && ++consumed == limit) { + consumed = 0; + upstream.request(limit); + } + + Stream stream; + try { + stream = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Stream"); + iterator = stream.iterator(); + + if (iterator.hasNext()) { + currentIterator = iterator; + currentCloseable = stream; + } else { + iterator = null; + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + trySignalError(downstream, ex); + } + continue; + } + } + if (iterator != null && emitted != requested) { + R item; + + try { + item = Objects.requireNonNull(iterator.next(), "The Stream.Iterator returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + trySignalError(downstream, ex); + continue; + } + + if (!cancelled) { + downstream.onNext(item); + emitted++; + + if (!cancelled) { + try { + if (!iterator.hasNext()) { + iterator = null; + clearCurrentRethrowCloseError(); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + trySignalError(downstream, ex); + } + } + } + + continue; + } + } + + this.emitted = emitted; + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + requested = this.requested.get(); + } + } + + void clearCurrentRethrowCloseError() throws Throwable { + currentIterator = null; + AutoCloseable ac = currentCloseable; + currentCloseable = null; + if (ac != null) { + ac.close(); + } + } + + void clearCurrentSuppressCloseError() { + try { + clearCurrentRethrowCloseError(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + + void trySignalError(Subscriber downstream, Throwable ex) { + if (error.compareAndSet(null, ex)) { + upstream.cancel(); + cancelled = true; + downstream.onError(ex); + } else { + RxJavaPlugins.onError(ex); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromCompletionStage.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromCompletionStage.java new file mode 100644 index 0000000000..1ee0f7786e --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromCompletionStage.java @@ -0,0 +1,95 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.internal.subscriptions.DeferredScalarSubscription; + +/** + * Wrap a CompletionStage and signal its outcome. + * @param the element type + * @since 3.0.0 + */ +public final class FlowableFromCompletionStage extends Flowable { + + final CompletionStage stage; + + public FlowableFromCompletionStage(CompletionStage stage) { + this.stage = stage; + } + + @Override + protected void subscribeActual(Subscriber s) { + // We need an indirection because one can't detach from a whenComplete + // and cancellation should not hold onto the stage. + BiConsumerAtomicReference whenReference = new BiConsumerAtomicReference<>(); + CompletionStageHandler handler = new CompletionStageHandler<>(s, whenReference); + whenReference.lazySet(handler); + + s.onSubscribe(handler); + stage.whenComplete(whenReference); + } + + static final class CompletionStageHandler + extends DeferredScalarSubscription + implements BiConsumer { + + private static final long serialVersionUID = 4665335664328839859L; + + final BiConsumerAtomicReference whenReference; + + CompletionStageHandler(Subscriber downstream, BiConsumerAtomicReference whenReference) { + super(downstream); + this.whenReference = whenReference; + } + + @Override + public void accept(T item, Throwable error) { + if (error != null) { + downstream.onError(error); + } + else if (item != null) { + complete(item); + } else { + downstream.onError(new NullPointerException("The CompletionStage terminated with null.")); + } + } + + @Override + public void cancel() { + super.cancel(); + whenReference.set(null); + } + } + + static final class BiConsumerAtomicReference extends AtomicReference> + implements BiConsumer { + + private static final long serialVersionUID = 45838553147237545L; + + @Override + public void accept(T t, Throwable u) { + BiConsumer biConsumer = get(); + if (biConsumer != null) { + biConsumer.accept(t, u); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromStream.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromStream.java new file mode 100644 index 0000000000..4fd3b4eb00 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromStream.java @@ -0,0 +1,319 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Iterator; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.annotations.*; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps a {@link Stream} and emits its values as a Flowable sequence. + * @param the element type of the Stream + * @since 3.0.0 + */ +public final class FlowableFromStream extends Flowable { + + final Stream stream; + + public FlowableFromStream(Stream stream) { + this.stream = stream; + } + + @Override + protected void subscribeActual(Subscriber s) { + subscribeStream(s, stream); + } + + /** + * Subscribes to the Stream by picking the normal or conditional stream Subscription implementation. + * @param the element type of the flow + * @param s the subscriber to drive + * @param stream the sequence to consume + */ + public static void subscribeStream(Subscriber s, Stream stream) { + Iterator iterator; + try { + iterator = stream.iterator(); + + if (!iterator.hasNext()) { + EmptySubscription.complete(s); + closeSafely(stream); + return; + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + closeSafely(stream); + return; + } + + if (s instanceof ConditionalSubscriber) { + s.onSubscribe(new StreamConditionalSubscription<>((ConditionalSubscriber)s, iterator, stream)); + } else { + s.onSubscribe(new StreamSubscription<>(s, iterator, stream)); + } + } + + static void closeSafely(AutoCloseable c) { + try { + c.close(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + + abstract static class AbstractStreamSubscription extends AtomicLong implements QueueSubscription { + + private static final long serialVersionUID = -9082954702547571853L; + + Iterator iterator; + + AutoCloseable closeable; + + volatile boolean cancelled; + + boolean once; + + AbstractStreamSubscription(Iterator iterator, AutoCloseable closeable) { + this.iterator = iterator; + this.closeable = closeable; + } + + @Override + public void request(long n) { + if (SubscriptionHelper.validate(n)) { + if (BackpressureHelper.add(this, n) == 0L) { + run(n); + } + } + } + + abstract void run(long n); + + @Override + public void cancel() { + cancelled = true; + request(1L); + } + + @Override + public int requestFusion(int mode) { + if ((mode & SYNC) != 0) { + lazySet(Long.MAX_VALUE); + return SYNC; + } + return NONE; + } + + @Override + public boolean offer(@NonNull T value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean offer(@NonNull T v1, @NonNull T v2) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public T poll() { + if (iterator == null) { + return null; + } + if (!once) { + once = true; + } else { + if (!iterator.hasNext()) { + clear(); + return null; + } + } + return Objects.requireNonNull(iterator.next(), "The Stream's Iterator.next() returned a null value"); + } + + @Override + public boolean isEmpty() { + Iterator it = iterator; + if (it != null) { + if (!once || it.hasNext()) { + return false; + } + clear(); + } + return true; + } + + @Override + public void clear() { + iterator = null; + AutoCloseable c = closeable; + closeable = null; + if (c != null) { + closeSafely(c); + } + } + } + + static final class StreamSubscription extends AbstractStreamSubscription { + + private static final long serialVersionUID = -9082954702547571853L; + + final Subscriber downstream; + + StreamSubscription(Subscriber downstream, Iterator iterator, AutoCloseable closeable) { + super(iterator, closeable); + this.downstream = downstream; + } + + @Override + public void run(long n) { + long emitted = 0L; + Iterator iterator = this.iterator; + Subscriber downstream = this.downstream; + + for (;;) { + + if (cancelled) { + clear(); + break; + } else { + T next; + try { + next = Objects.requireNonNull(iterator.next(), "The Stream's Iterator returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + cancelled = true; + continue; + } + + downstream.onNext(next); + + if (cancelled) { + continue; + } + + try { + if (!iterator.hasNext()) { + downstream.onComplete(); + cancelled = true; + continue; + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + cancelled = true; + continue; + } + + if (++emitted != n) { + continue; + } + } + + n = get(); + if (emitted == n) { + if (compareAndSet(n, 0L)) { + break; + } + n = get(); + } + } + } + } + + static final class StreamConditionalSubscription extends AbstractStreamSubscription { + + private static final long serialVersionUID = -9082954702547571853L; + + final ConditionalSubscriber downstream; + + StreamConditionalSubscription(ConditionalSubscriber downstream, Iterator iterator, AutoCloseable closeable) { + super(iterator, closeable); + this.downstream = downstream; + } + + @Override + public void run(long n) { + long emitted = 0L; + Iterator iterator = this.iterator; + ConditionalSubscriber downstream = this.downstream; + + for (;;) { + + if (cancelled) { + clear(); + break; + } else { + T next; + try { + next = Objects.requireNonNull(iterator.next(), "The Stream's Iterator returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + cancelled = true; + continue; + } + + if (downstream.tryOnNext(next)) { + emitted++; + } + + if (cancelled) { + continue; + } + + try { + if (!iterator.hasNext()) { + downstream.onComplete(); + cancelled = true; + continue; + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + cancelled = true; + continue; + } + + if (emitted != n) { + continue; + } + } + + n = get(); + if (emitted == n) { + if (compareAndSet(n, 0L)) { + break; + } + n = get(); + } + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableLastStageSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableLastStageSubscriber.java new file mode 100644 index 0000000000..0bdf3e9304 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableLastStageSubscriber.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; + +import org.reactivestreams.Subscription; + +/** + * Signals the last element of the source via the underlying CompletableFuture, + * signals the a default item if the upstream is empty or signals {@link NoSuchElementException}. + * + * @param the element type + * @since 3.0.0 + */ +public final class FlowableLastStageSubscriber extends FlowableStageSubscriber { + + final boolean hasDefault; + + final T defaultItem; + + public FlowableLastStageSubscriber(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + } + + @Override + public void onNext(T t) { + value = t; + } + + @Override + public void onComplete() { + if (!isDone()) { + T v = value; + clear(); + if (v != null) { + complete(v); + } else if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException()); + } + } + } + + @Override + protected void afterSubscribe(Subscription s) { + s.request(Long.MAX_VALUE); + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptional.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptional.java new file mode 100644 index 0000000000..cc4ff8bdf5 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptional.java @@ -0,0 +1,180 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.subscribers.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; + +/** + * Map the upstream values into an Optional and emit its value if any. + * @param the upstream element type + * @param the output element type + * @since 3.0.0 + */ +public final class FlowableMapOptional extends Flowable { + + final Flowable source; + + final Function> mapper; + + public FlowableMapOptional(Flowable source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(Subscriber s) { + if (s instanceof ConditionalSubscriber) { + source.subscribe(new MapOptionalConditionalSubscriber<>((ConditionalSubscriber)s, mapper)); + } else { + source.subscribe(new MapOptionalSubscriber<>(s, mapper)); + } + } + + static final class MapOptionalSubscriber extends BasicFuseableSubscriber + implements ConditionalSubscriber { + + final Function> mapper; + + MapOptionalSubscriber(Subscriber downstream, Function> mapper) { + super(downstream); + this.mapper = mapper; + } + + @Override + public void onNext(T t) { + if (!tryOnNext(t)) { + upstream.request(1); + } + } + + @Override + public boolean tryOnNext(T t) { + if (done) { + return true; + } + + if (sourceMode != NONE) { + downstream.onNext(null); + return true; + } + + Optional result; + try { + result = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Optional"); + } catch (Throwable ex) { + fail(ex); + return true; + } + + if (result.isPresent()) { + downstream.onNext(result.get()); + return true; + } + return false; + } + + @Override + public int requestFusion(int mode) { + return transitiveBoundaryFusion(mode); + } + + @Override + public R poll() throws Throwable { + for (;;) { + T item = qs.poll(); + if (item == null) { + return null; + } + Optional result = Objects.requireNonNull(mapper.apply(item), "The mapper returned a null Optional"); + if (result.isPresent()) { + return result.get(); + } + if (sourceMode == ASYNC) { + qs.request(1); + } + } + } + } + + static final class MapOptionalConditionalSubscriber extends BasicFuseableConditionalSubscriber { + + final Function> mapper; + + MapOptionalConditionalSubscriber(ConditionalSubscriber downstream, Function> mapper) { + super(downstream); + this.mapper = mapper; + } + + @Override + public void onNext(T t) { + if (!tryOnNext(t)) { + upstream.request(1); + } + } + + @Override + public boolean tryOnNext(T t) { + if (done) { + return true; + } + + if (sourceMode != NONE) { + downstream.onNext(null); + return true; + } + + Optional result; + try { + result = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Optional"); + } catch (Throwable ex) { + fail(ex); + return true; + } + + if (result.isPresent()) { + return downstream.tryOnNext(result.get()); + } + return false; + } + + @Override + public int requestFusion(int mode) { + return transitiveBoundaryFusion(mode); + } + + @Override + public R poll() throws Throwable { + for (;;) { + T item = qs.poll(); + if (item == null) { + return null; + } + Optional result = Objects.requireNonNull(mapper.apply(item), "The mapper returned a null Optional"); + if (result.isPresent()) { + return result.get(); + } + if (sourceMode == ASYNC) { + qs.request(1); + } + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableSingleStageSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableSingleStageSubscriber.java new file mode 100644 index 0000000000..8206a9a8ac --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableSingleStageSubscriber.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; + +import org.reactivestreams.Subscription; + +/** + * Signals the only element of the source via the underlying CompletableFuture, + * signals the a default item if the upstream is empty or signals {@link IllegalArgumentException} + * if the upstream has more than one item. + * + * @param the element type + * @since 3.0.0 + */ +public final class FlowableSingleStageSubscriber extends FlowableStageSubscriber { + + final boolean hasDefault; + + final T defaultItem; + + public FlowableSingleStageSubscriber(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + } + + @Override + public void onNext(T t) { + if (value != null) { + value = null; + completeExceptionally(new IllegalArgumentException("Sequence contains more than one element!")); + } else { + value = t; + } + } + + @Override + public void onComplete() { + if (!isDone()) { + T v = value; + clear(); + if (v != null) { + complete(v); + } else if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException()); + } + } + } + + @Override + protected void afterSubscribe(Subscription s) { + s.request(2); + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriber.java new file mode 100644 index 0000000000..66e4fddde7 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriber.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Base class that extends CompletableFuture and provides basic infrastructure + * to notify watchers upon upstream signals. + * @param the element type + * @since 3.0.0 + */ +abstract class FlowableStageSubscriber extends CompletableFuture implements FlowableSubscriber { + + final AtomicReference upstream = new AtomicReference<>(); + + T value; + + @Override + public final void onSubscribe(@NonNull Subscription s) { + if (SubscriptionHelper.setOnce(upstream, s)) { + afterSubscribe(s); + } + } + + protected abstract void afterSubscribe(Subscription s); + + @Override + public final void onError(Throwable t) { + clear(); + if (!completeExceptionally(t)) { + RxJavaPlugins.onError(t); + } + } + + protected final void cancelUpstream() { + SubscriptionHelper.cancel(upstream); + } + + protected final void clear() { + value = null; + upstream.lazySet(SubscriptionHelper.CANCELLED); + } + + @Override + public final boolean cancel(boolean mayInterruptIfRunning) { + cancelUpstream(); + return super.cancel(mayInterruptIfRunning); + } + + @Override + public final boolean complete(T value) { + cancelUpstream(); + return super.complete(value); + } + + @Override + public final boolean completeExceptionally(Throwable ex) { + cancelUpstream(); + return super.completeExceptionally(ex); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowable.java new file mode 100644 index 0000000000..653642426e --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowable.java @@ -0,0 +1,285 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.annotations.*; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Map the success value into a Java {@link Stream} and emits its values. + * + * @param the source value type + * @param the output value type + * @since 3.0.0 + */ +public final class MaybeFlattenStreamAsFlowable extends Flowable { + + final Maybe source; + + final Function> mapper; + + public MaybeFlattenStreamAsFlowable(Maybe source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(@NonNull Subscriber s) { + source.subscribe(new FlattenStreamMultiObserver<>(s, mapper)); + } + + static final class FlattenStreamMultiObserver + extends BasicIntQueueSubscription + implements MaybeObserver, SingleObserver { + + private static final long serialVersionUID = 7363336003027148283L; + + final Subscriber downstream; + + final Function> mapper; + + final AtomicLong requested; + + Disposable upstream; + + volatile Iterator iterator; + + AutoCloseable close; + + boolean once; + + volatile boolean cancelled; + + boolean outputFused; + + long emitted; + + FlattenStreamMultiObserver(Subscriber downstream, Function> mapper) { + this.downstream = downstream; + this.mapper = mapper; + this.requested = new AtomicLong(); + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(@NonNull T t) { + try { + Stream stream = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Stream"); + Iterator iterator = stream.iterator(); + AutoCloseable c = stream; + + if (!iterator.hasNext()) { + downstream.onComplete(); + close(c); + return; + } + this.iterator = iterator; + this.close = stream; + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + drain(); + } + + @Override + public void onError(@NonNull Throwable e) { + downstream.onError(e); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public void request(long n) { + if (SubscriptionHelper.validate(n)) { + BackpressureHelper.add(requested, n); + drain(); + } + } + + @Override + public void cancel() { + cancelled = true; + upstream.dispose(); + if (!outputFused) { + drain(); + } + } + + @Override + public int requestFusion(int mode) { + if ((mode & ASYNC) != 0) { + outputFused = true; + return ASYNC; + } + return NONE; + } + + @Override + public @Nullable R poll() throws Throwable { + Iterator it = iterator; + if (it != null) { + if (once) { + if (!it.hasNext()) { + clear(); + return null; + } + } else { + once = true; + } + return it.next(); + } + return null; + } + + @Override + public boolean isEmpty() { + Iterator it = iterator; + if (it != null) { + if (!once) { + return false; + } + if (it.hasNext()) { + return false; + } + clear(); + } + return true; + } + + @Override + public void clear() { + iterator = null; + AutoCloseable close = this.close; + this.close = null; + close(close); + } + + void close(AutoCloseable c) { + try { + if (c != null) { + c.close(); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + + void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + Subscriber downstream = this.downstream; + long emitted = this.emitted; + long requested = this.requested.get(); + Iterator it = iterator; + + for (;;) { + + if (cancelled) { + clear(); + } else { + if (outputFused) { + if (it != null) { + downstream.onNext(null); + downstream.onComplete(); + } + } else { + if (it != null && emitted != requested) { + R item; + try { + item = it.next(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + cancelled = true; + continue; + } + + if (cancelled) { + continue; + } + + downstream.onNext(item); + emitted++; + + if (cancelled) { + continue; + } + + boolean has; + try { + has = it.hasNext(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + cancelled = true; + continue; + } + + if (cancelled) { + continue; + } + + if (!has) { + downstream.onComplete(); + cancelled = true; + } + continue; + } + } + } + + this.emitted = emitted; + missed = addAndGet(-missed); + if (missed == 0) { + return; + } + + requested = this.requested.get(); + if (it == null) { + it = iterator; + } + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsObservable.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsObservable.java new file mode 100644 index 0000000000..404fcb8106 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsObservable.java @@ -0,0 +1,262 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; +import java.util.stream.Stream; + +import io.reactivex.rxjava3.annotations.*; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.observers.BasicIntQueueDisposable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Map the success value into a Java {@link Stream} and emits its values. + * + * @param the source value type + * @param the output value type + * @since 3.0.0 + */ +public final class MaybeFlattenStreamAsObservable extends Observable { + + final Maybe source; + + final Function> mapper; + + public MaybeFlattenStreamAsObservable(Maybe source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(@NonNull Observer s) { + source.subscribe(new FlattenStreamMultiObserver<>(s, mapper)); + } + + static final class FlattenStreamMultiObserver + extends BasicIntQueueDisposable + implements MaybeObserver, SingleObserver { + + private static final long serialVersionUID = 7363336003027148283L; + + final Observer downstream; + + final Function> mapper; + + Disposable upstream; + + volatile Iterator iterator; + + AutoCloseable close; + + boolean once; + + volatile boolean disposed; + + boolean outputFused; + + FlattenStreamMultiObserver(Observer downstream, Function> mapper) { + this.downstream = downstream; + this.mapper = mapper; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(@NonNull T t) { + try { + Stream stream = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Stream"); + Iterator iterator = stream.iterator(); + AutoCloseable c = stream; + + if (!iterator.hasNext()) { + downstream.onComplete(); + close(c); + return; + } + this.iterator = iterator; + this.close = stream; + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + drain(); + } + + @Override + public void onError(@NonNull Throwable e) { + downstream.onError(e); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public void dispose() { + disposed = true; + upstream.dispose(); + if (!outputFused) { + drain(); + } + } + + @Override + public boolean isDisposed() { + return disposed; + } + + @Override + public int requestFusion(int mode) { + if ((mode & ASYNC) != 0) { + outputFused = true; + return ASYNC; + } + return NONE; + } + + @Override + public @Nullable R poll() throws Throwable { + Iterator it = iterator; + if (it != null) { + if (once) { + if (!it.hasNext()) { + clear(); + return null; + } + } else { + once = true; + } + return it.next(); + } + return null; + } + + @Override + public boolean isEmpty() { + Iterator it = iterator; + if (it != null) { + if (!once) { + return false; + } + if (it.hasNext()) { + return false; + } + clear(); + } + return true; + } + + @Override + public void clear() { + iterator = null; + AutoCloseable close = this.close; + this.close = null; + close(close); + } + + void close(AutoCloseable c) { + try { + if (c != null) { + c.close(); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + + void drain() { + if (getAndIncrement() != 0) { + return; + } + + int missed = 1; + Observer downstream = this.downstream; + Iterator it = iterator; + + for (;;) { + + if (disposed) { + clear(); + } else { + if (outputFused) { + downstream.onNext(null); + downstream.onComplete(); + } else { + R item; + try { + item = it.next(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + disposed = true; + continue; + } + + if (disposed) { + continue; + } + + downstream.onNext(item); + + if (disposed) { + continue; + } + + boolean has; + try { + has = it.hasNext(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + disposed = true; + continue; + } + + if (disposed) { + continue; + } + + if (!has) { + downstream.onComplete(); + disposed = true; + } + continue; + } + } + + missed = addAndGet(-missed); + if (missed == 0) { + return; + } + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromCompletionStage.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromCompletionStage.java new file mode 100644 index 0000000000..45af605e96 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromCompletionStage.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.jdk8.FlowableFromCompletionStage.BiConsumerAtomicReference; + +/** + * Wrap a CompletionStage and signal its outcome. + * @param the element type + * @since 3.0.0 + */ +public final class MaybeFromCompletionStage extends Maybe { + + final CompletionStage stage; + + public MaybeFromCompletionStage(CompletionStage stage) { + this.stage = stage; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + // We need an indirection because one can't detach from a whenComplete + // and cancellation should not hold onto the stage. + BiConsumerAtomicReference whenReference = new BiConsumerAtomicReference<>(); + CompletionStageHandler handler = new CompletionStageHandler<>(observer, whenReference); + whenReference.lazySet(handler); + + observer.onSubscribe(handler); + stage.whenComplete(whenReference); + } + + static final class CompletionStageHandler + implements Disposable, BiConsumer { + + final MaybeObserver downstream; + + final BiConsumerAtomicReference whenReference; + + CompletionStageHandler(MaybeObserver downstream, BiConsumerAtomicReference whenReference) { + this.downstream = downstream; + this.whenReference = whenReference; + } + + @Override + public void accept(T item, Throwable error) { + if (error != null) { + downstream.onError(error); + } + else if (item != null) { + downstream.onSuccess(item); + } else { + downstream.onComplete(); + } + } + + @Override + public void dispose() { + whenReference.set(null); + } + + @Override + public boolean isDisposed() { + return whenReference.get() == null; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeMapOptional.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeMapOptional.java new file mode 100644 index 0000000000..760456f2ee --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/MaybeMapOptional.java @@ -0,0 +1,110 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Maps the success value to an {@link Optional} and emits its non-empty value or completes. + * + * @param the upstream success value type + * @param the result value type + * @since 3.0.0 + */ +public final class MaybeMapOptional extends Maybe { + + final Maybe source; + + final Function> mapper; + + public MaybeMapOptional(Maybe source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new MapOptionalMaybeObserver<>(observer, mapper)); + } + + static final class MapOptionalMaybeObserver implements MaybeObserver, Disposable { + + final MaybeObserver downstream; + + final Function> mapper; + + Disposable upstream; + + MapOptionalMaybeObserver(MaybeObserver downstream, Function> mapper) { + this.downstream = downstream; + this.mapper = mapper; + } + + @Override + public void dispose() { + Disposable d = this.upstream; + this.upstream = DisposableHelper.DISPOSED; + d.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(T value) { + Optional v; + + try { + v = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null item"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + if (v.isPresent()) { + downstream.onSuccess(v.get()); + } else { + downstream.onComplete(); + } + } + + @Override + public void onError(Throwable e) { + downstream.onError(e); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java new file mode 100644 index 0000000000..980317750e --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollector.java @@ -0,0 +1,152 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Objects; +import java.util.function.*; +import java.util.stream.Collector; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.disposables.*; +import io.reactivex.rxjava3.internal.observers.DeferredScalarDisposable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Collect items into a container defined by a Stream {@link Collector} callback set. + * + * @param the upstream value type + * @param the intermediate accumulator type + * @param the result type + * @since 3.0.0 + */ +public final class ObservableCollectWithCollector extends Observable { + + final Observable source; + + final Collector collector; + + public ObservableCollectWithCollector(Observable source, Collector collector) { + this.source = source; + this.collector = collector; + } + + @Override + protected void subscribeActual(@NonNull Observer observer) { + A container; + BiConsumer accumulator; + Function finisher; + + try { + container = collector.supplier().get(); + accumulator = collector.accumulator(); + finisher = collector.finisher(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + return; + } + + source.subscribe(new CollectorObserver<>(observer, container, accumulator, finisher)); + } + + static final class CollectorObserver + extends DeferredScalarDisposable + implements Observer { + + private static final long serialVersionUID = -229544830565448758L; + + final BiConsumer accumulator; + + final Function finisher; + + Disposable upstream; + + boolean done; + + A container; + + CollectorObserver(Observer downstream, A container, BiConsumer accumulator, Function finisher) { + super(downstream); + this.container = container; + this.accumulator = accumulator; + this.finisher = finisher; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T t) { + if (done) { + return; + } + try { + accumulator.accept(container, t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + onError(ex); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + } else { + done = true; + upstream = DisposableHelper.DISPOSED; + this.container = null; + downstream.onError(t); + } + } + + @Override + public void onComplete() { + if (done) { + return; + } + + done = true; + upstream = DisposableHelper.DISPOSED; + A container = this.container; + this.container = null; + R result; + try { + result = Objects.requireNonNull(finisher.apply(container), "The finisher returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + complete(result); + } + + @Override + public void dispose() { + super.dispose(); + upstream.dispose(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java new file mode 100644 index 0000000000..fa1ccd9c8f --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorSingle.java @@ -0,0 +1,160 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Objects; +import java.util.function.*; +import java.util.stream.Collector; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.disposables.*; +import io.reactivex.rxjava3.internal.fuseable.FuseToObservable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Collect items into a container defined by a Stream {@link Collector} callback set. + * + * @param the upstream value type + * @param the intermediate accumulator type + * @param the result type + * @since 3.0.0 + */ +public final class ObservableCollectWithCollectorSingle extends Single implements FuseToObservable { + + final Observable source; + + final Collector collector; + + public ObservableCollectWithCollectorSingle(Observable source, Collector collector) { + this.source = source; + this.collector = collector; + } + + @Override + public Observable fuseToObservable() { + return new ObservableCollectWithCollector<>(source, collector); + } + + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + A container; + BiConsumer accumulator; + Function finisher; + + try { + container = collector.supplier().get(); + accumulator = collector.accumulator(); + finisher = collector.finisher(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + return; + } + + source.subscribe(new CollectorSingleObserver<>(observer, container, accumulator, finisher)); + } + + static final class CollectorSingleObserver implements Observer, Disposable { + + final SingleObserver downstream; + + final BiConsumer accumulator; + + final Function finisher; + + Disposable upstream; + + boolean done; + + A container; + + CollectorSingleObserver(SingleObserver downstream, A container, BiConsumer accumulator, Function finisher) { + this.downstream = downstream; + this.container = container; + this.accumulator = accumulator; + this.finisher = finisher; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T t) { + if (done) { + return; + } + try { + accumulator.accept(container, t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + onError(ex); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + } else { + done = true; + upstream = DisposableHelper.DISPOSED; + this.container = null; + downstream.onError(t); + } + } + + @Override + public void onComplete() { + if (done) { + return; + } + + done = true; + upstream = DisposableHelper.DISPOSED; + A container = this.container; + this.container = null; + R result; + try { + result = Objects.requireNonNull(finisher.apply(container), "The finisher returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + downstream.onSuccess(result); + } + + @Override + public void dispose() { + upstream.dispose(); + upstream = DisposableHelper.DISPOSED; + } + + @Override + public boolean isDisposed() { + return upstream == DisposableHelper.DISPOSED; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFirstStageObserver.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFirstStageObserver.java new file mode 100644 index 0000000000..6ac5ae51f1 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFirstStageObserver.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; + +/** + * Signals the first element of the source via the underlying CompletableFuture, + * signals the a default item if the upstream is empty or signals {@link NoSuchElementException}. + * + * @param the element type + * @since 3.0.0 + */ +public final class ObservableFirstStageObserver extends ObservableStageObserver { + + final boolean hasDefault; + + final T defaultItem; + + public ObservableFirstStageObserver(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + } + + @Override + public void onNext(T t) { + complete(t); + } + + @Override + public void onComplete() { + if (!isDone()) { + clear(); + if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException()); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFlatMapStream.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFlatMapStream.java new file mode 100644 index 0000000000..72bbea2a3f --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFlatMapStream.java @@ -0,0 +1,163 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Maps the upstream values onto {@link Stream}s and emits their items in order to the downstream. + * + * @param the upstream element type + * @param the inner {@code Stream} and result element type + * @since 3.0.0 + */ +public final class ObservableFlatMapStream extends Observable { + + final Observable source; + + final Function> mapper; + + public ObservableFlatMapStream(Observable source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(Observer observer) { + if (source instanceof Supplier) { + Stream stream = null; + try { + @SuppressWarnings("unchecked") + T t = ((Supplier)source).get(); + if (t != null) { + stream = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Stream"); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + return; + } + + if (stream != null) { + ObservableFromStream.subscribeStream(observer, stream); + } else { + EmptyDisposable.complete(observer); + } + } else { + source.subscribe(new FlatMapStreamObserver<>(observer, mapper)); + } + } + + static final class FlatMapStreamObserver extends AtomicInteger + implements Observer, Disposable { + + private static final long serialVersionUID = -5127032662980523968L; + + final Observer downstream; + + final Function> mapper; + + Disposable upstream; + + volatile boolean disposed; + + boolean done; + + FlatMapStreamObserver(Observer downstream, Function> mapper) { + this.downstream = downstream; + this.mapper = mapper; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(@NonNull T t) { + if (done) { + return; + } + try { + try (Stream stream = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Stream")) { + Iterator it = stream.iterator(); + while (it.hasNext()) { + if (disposed) { + done = true; + break; + } + R value = Objects.requireNonNull(it.next(), "The Stream's Iterator.next returned a null value"); + if (disposed) { + done = true; + break; + } + downstream.onNext(value); + if (disposed) { + done = true; + break; + } + } + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + onError(ex); + } + } + + @Override + public void onError(@NonNull Throwable e) { + if (done) { + RxJavaPlugins.onError(e); + } else { + done = true; + downstream.onError(e); + } + } + + @Override + public void onComplete() { + if (!done) { + done = true; + downstream.onComplete(); + } + } + + @Override + public void dispose() { + disposed = true; + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return disposed; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromCompletionStage.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromCompletionStage.java new file mode 100644 index 0000000000..11a5a6307c --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromCompletionStage.java @@ -0,0 +1,93 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.internal.observers.DeferredScalarDisposable; + +/** + * Wrap a CompletionStage and signal its outcome. + * @param the element type + * @since 3.0.0 + */ +public final class ObservableFromCompletionStage extends Observable { + + final CompletionStage stage; + + public ObservableFromCompletionStage(CompletionStage stage) { + this.stage = stage; + } + + @Override + protected void subscribeActual(Observer observer) { + // We need an indirection because one can't detach from a whenComplete + // and cancellation should not hold onto the stage. + BiConsumerAtomicReference whenReference = new BiConsumerAtomicReference<>(); + CompletionStageHandler handler = new CompletionStageHandler<>(observer, whenReference); + whenReference.lazySet(handler); + + observer.onSubscribe(handler); + stage.whenComplete(whenReference); + } + + static final class CompletionStageHandler + extends DeferredScalarDisposable + implements BiConsumer { + + private static final long serialVersionUID = 4665335664328839859L; + + final BiConsumerAtomicReference whenReference; + + CompletionStageHandler(Observer downstream, BiConsumerAtomicReference whenReference) { + super(downstream); + this.whenReference = whenReference; + } + + @Override + public void accept(T item, Throwable error) { + if (error != null) { + downstream.onError(error); + } + else if (item != null) { + complete(item); + } else { + downstream.onError(new NullPointerException("The CompletionStage terminated with null.")); + } + } + + @Override + public void dispose() { + super.dispose(); + whenReference.set(null); + } + } + + static final class BiConsumerAtomicReference extends AtomicReference> + implements BiConsumer { + + private static final long serialVersionUID = 45838553147237545L; + + @Override + public void accept(T t, Throwable u) { + BiConsumer biConsumer = get(); + if (biConsumer != null) { + biConsumer.accept(t, u); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromStream.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromStream.java new file mode 100644 index 0000000000..169eeb0bcc --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromStream.java @@ -0,0 +1,220 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; +import java.util.stream.Stream; + +import io.reactivex.rxjava3.annotations.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps a {@link Stream} and emits its values as an {@link Observable} sequence. + * @param the element type of the Stream + * @since 3.0.0 + */ +public final class ObservableFromStream extends Observable { + + final Stream stream; + + public ObservableFromStream(Stream stream) { + this.stream = stream; + } + + @Override + protected void subscribeActual(Observer observer) { + subscribeStream(observer, stream); + } + + /** + * Subscribes to the Stream. + * @param the element type of the flow + * @param observer the observer to drive + * @param stream the sequence to consume + */ + public static void subscribeStream(Observer observer, Stream stream) { + Iterator iterator; + try { + iterator = stream.iterator(); + + if (!iterator.hasNext()) { + EmptyDisposable.complete(observer); + closeSafely(stream); + return; + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + closeSafely(stream); + return; + } + + StreamDisposable disposable = new StreamDisposable<>(observer, iterator, stream); + observer.onSubscribe(disposable); + disposable.run(); + } + + static void closeSafely(AutoCloseable c) { + try { + c.close(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + + static final class StreamDisposable implements QueueDisposable { + + final Observer downstream; + + Iterator iterator; + + AutoCloseable closeable; + + volatile boolean disposed; + + boolean once; + + boolean outputFused; + + StreamDisposable(Observer downstream, Iterator iterator, AutoCloseable closeable) { + this.downstream = downstream; + this.iterator = iterator; + this.closeable = closeable; + } + + @Override + public void dispose() { + disposed = true; + run(); + } + + @Override + public boolean isDisposed() { + return disposed; + } + + @Override + public int requestFusion(int mode) { + if ((mode & SYNC) != 0) { + outputFused = true; + return SYNC; + } + return NONE; + } + + @Override + public boolean offer(@NonNull T value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean offer(@NonNull T v1, @NonNull T v2) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public T poll() { + if (iterator == null) { + return null; + } + if (!once) { + once = true; + } else { + if (!iterator.hasNext()) { + clear(); + return null; + } + } + return Objects.requireNonNull(iterator.next(), "The Stream's Iterator.next() returned a null value"); + } + + @Override + public boolean isEmpty() { + Iterator it = iterator; + if (it != null) { + if (!once || it.hasNext()) { + return false; + } + clear(); + } + return true; + } + + @Override + public void clear() { + iterator = null; + AutoCloseable c = closeable; + closeable = null; + if (c != null) { + closeSafely(c); + } + } + + public void run() { + if (outputFused) { + return; + } + Iterator iterator = this.iterator; + Observer downstream = this.downstream; + + for (;;) { + if (disposed) { + clear(); + break; + } + + T next; + try { + next = Objects.requireNonNull(iterator.next(), "The Stream's Iterator.next returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + disposed = true; + continue; + } + + if (disposed) { + continue; + } + + downstream.onNext(next); + + if (disposed) { + continue; + } + + try { + if (iterator.hasNext()) { + continue; + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + disposed = true; + continue; + } + + downstream.onComplete(); + disposed = true; + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableLastStageObserver.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableLastStageObserver.java new file mode 100644 index 0000000000..b35ab3dba9 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableLastStageObserver.java @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; + +/** + * Signals the last element of the source via the underlying CompletableFuture, + * signals the a default item if the upstream is empty or signals {@link NoSuchElementException}. + * + * @param the element type + * @since 3.0.0 + */ +public final class ObservableLastStageObserver extends ObservableStageObserver { + + final boolean hasDefault; + + final T defaultItem; + + public ObservableLastStageObserver(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + } + + @Override + public void onNext(T t) { + value = t; + } + + @Override + public void onComplete() { + if (!isDone()) { + T v = value; + clear(); + if (v != null) { + complete(v); + } else if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException()); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableMapOptional.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableMapOptional.java new file mode 100644 index 0000000000..a51ef1af28 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableMapOptional.java @@ -0,0 +1,97 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.observers.BasicFuseableObserver; + +/** + * Map the upstream values into an Optional and emit its value if any. + * @param the upstream element type + * @param the output element type + * @since 3.0.0 + */ +public final class ObservableMapOptional extends Observable { + + final Observable source; + + final Function> mapper; + + public ObservableMapOptional(Observable source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(Observer observer) { + source.subscribe(new MapOptionalObserver<>(observer, mapper)); + } + + static final class MapOptionalObserver extends BasicFuseableObserver { + + final Function> mapper; + + MapOptionalObserver(Observer downstream, Function> mapper) { + super(downstream); + this.mapper = mapper; + } + + @Override + public void onNext(T t) { + if (done) { + return; + } + + if (sourceMode != NONE) { + downstream.onNext(null); + return; + } + + Optional result; + try { + result = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Optional"); + } catch (Throwable ex) { + fail(ex); + return; + } + + if (result.isPresent()) { + downstream.onNext(result.get()); + } + } + + @Override + public int requestFusion(int mode) { + return transitiveBoundaryFusion(mode); + } + + @Override + public R poll() throws Throwable { + for (;;) { + T item = qd.poll(); + if (item == null) { + return null; + } + Optional result = Objects.requireNonNull(mapper.apply(item), "The mapper returned a null Optional"); + if (result.isPresent()) { + return result.get(); + } + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableSingleStageObserver.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableSingleStageObserver.java new file mode 100644 index 0000000000..c506fc36d4 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableSingleStageObserver.java @@ -0,0 +1,61 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.NoSuchElementException; + +/** + * Signals the only element of the source via the underlying CompletableFuture, + * signals the a default item if the upstream is empty or signals {@link IllegalArgumentException} + * if the upstream has more than one item. + * + * @param the element type + * @since 3.0.0 + */ +public final class ObservableSingleStageObserver extends ObservableStageObserver { + + final boolean hasDefault; + + final T defaultItem; + + public ObservableSingleStageObserver(boolean hasDefault, T defaultItem) { + this.hasDefault = hasDefault; + this.defaultItem = defaultItem; + } + + @Override + public void onNext(T t) { + if (value != null) { + value = null; + completeExceptionally(new IllegalArgumentException("Sequence contains more than one element!")); + } else { + value = t; + } + } + + @Override + public void onComplete() { + if (!isDone()) { + T v = value; + clear(); + if (v != null) { + complete(v); + } else if (hasDefault) { + complete(defaultItem); + } else { + completeExceptionally(new NoSuchElementException()); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageObserver.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageObserver.java new file mode 100644 index 0000000000..4025890f17 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageObserver.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Base class that extends CompletableFuture and provides basic infrastructure + * to notify watchers upon upstream signals. + * @param the element type + * @since 3.0.0 + */ +abstract class ObservableStageObserver extends CompletableFuture implements Observer { + + final AtomicReference upstream = new AtomicReference<>(); + + T value; + + @Override + public final void onSubscribe(@NonNull Disposable d) { + DisposableHelper.setOnce(upstream, d); + } + + @Override + public final void onError(Throwable t) { + clear(); + if (!completeExceptionally(t)) { + RxJavaPlugins.onError(t); + } + } + + protected final void disposeUpstream() { + DisposableHelper.dispose(upstream); + } + + protected final void clear() { + value = null; + upstream.lazySet(DisposableHelper.DISPOSED); + } + + @Override + public final boolean cancel(boolean mayInterruptIfRunning) { + disposeUpstream(); + return super.cancel(mayInterruptIfRunning); + } + + @Override + public final boolean complete(T value) { + disposeUpstream(); + return super.complete(value); + } + + @Override + public final boolean completeExceptionally(Throwable ex) { + disposeUpstream(); + return super.completeExceptionally(ex); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollector.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollector.java new file mode 100644 index 0000000000..7fefa11e80 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollector.java @@ -0,0 +1,269 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Objects; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.stream.Collector; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Reduces all 'rails' into a single via a Java 8 {@link Collector} callback set. + * + * @param the value type + * @param the accumulator type + * @param the result type + * @since 3.0.0 + */ +public final class ParallelCollector extends Flowable { + + final ParallelFlowable source; + + final Collector collector; + + public ParallelCollector(ParallelFlowable source, Collector collector) { + this.source = source; + this.collector = collector; + } + + @Override + protected void subscribeActual(Subscriber s) { + ParallelCollectorSubscriber parent; + try { + parent = new ParallelCollectorSubscriber<>(s, source.parallelism(), collector); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + return; + } + s.onSubscribe(parent); + + source.subscribe(parent.subscribers); + } + + static final class ParallelCollectorSubscriber extends DeferredScalarSubscription { + + private static final long serialVersionUID = -5370107872170712765L; + + final ParallelCollectorInnerSubscriber[] subscribers; + + final AtomicReference> current = new AtomicReference<>(); + + final AtomicInteger remaining = new AtomicInteger(); + + final AtomicThrowable error = new AtomicThrowable(); + + final Function finisher; + + ParallelCollectorSubscriber(Subscriber subscriber, int n, Collector collector) { + super(subscriber); + this.finisher = collector.finisher(); + @SuppressWarnings("unchecked") + ParallelCollectorInnerSubscriber[] a = new ParallelCollectorInnerSubscriber[n]; + for (int i = 0; i < n; i++) { + a[i] = new ParallelCollectorInnerSubscriber<>(this, collector.supplier().get(), collector.accumulator(), collector.combiner()); + } + this.subscribers = a; + remaining.lazySet(n); + } + + SlotPair addValue(A value) { + for (;;) { + SlotPair curr = current.get(); + + if (curr == null) { + curr = new SlotPair<>(); + if (!current.compareAndSet(null, curr)) { + continue; + } + } + + int c = curr.tryAcquireSlot(); + if (c < 0) { + current.compareAndSet(curr, null); + continue; + } + if (c == 0) { + curr.first = value; + } else { + curr.second = value; + } + + if (curr.releaseSlot()) { + current.compareAndSet(curr, null); + return curr; + } + return null; + } + } + + @Override + public void cancel() { + for (ParallelCollectorInnerSubscriber inner : subscribers) { + inner.cancel(); + } + } + + void innerError(Throwable ex) { + if (error.compareAndSet(null, ex)) { + cancel(); + downstream.onError(ex); + } else { + if (ex != error.get()) { + RxJavaPlugins.onError(ex); + } + } + } + + void innerComplete(A value, BinaryOperator combiner) { + for (;;) { + SlotPair sp = addValue(value); + + if (sp != null) { + + try { + value = combiner.apply(sp.first, sp.second); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + innerError(ex); + return; + } + + } else { + break; + } + } + + if (remaining.decrementAndGet() == 0) { + SlotPair sp = current.get(); + current.lazySet(null); + + R result; + try { + result = Objects.requireNonNull(finisher.apply(sp.first), "The finisher returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + innerError(ex); + return; + } + + complete(result); + } + } + } + + static final class ParallelCollectorInnerSubscriber + extends AtomicReference + implements FlowableSubscriber { + + private static final long serialVersionUID = -7954444275102466525L; + + final ParallelCollectorSubscriber parent; + + final BiConsumer accumulator; + + final BinaryOperator combiner; + + A container; + + boolean done; + + ParallelCollectorInnerSubscriber(ParallelCollectorSubscriber parent, A container, BiConsumer accumulator, BinaryOperator combiner) { + this.parent = parent; + this.accumulator = accumulator; + this.combiner = combiner; + this.container = container; + } + + @Override + public void onSubscribe(Subscription s) { + SubscriptionHelper.setOnce(this, s, Long.MAX_VALUE); + } + + @Override + public void onNext(T t) { + if (!done) { + try { + accumulator.accept(container, t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + get().cancel(); + onError(ex); + } + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + return; + } + container = null; + done = true; + parent.innerError(t); + } + + @Override + public void onComplete() { + if (!done) { + A v = container; + container = null; + done = true; + parent.innerComplete(v, combiner); + } + } + + void cancel() { + SubscriptionHelper.cancel(this); + } + } + + static final class SlotPair extends AtomicInteger { + + private static final long serialVersionUID = 473971317683868662L; + + T first; + + T second; + + final AtomicInteger releaseIndex = new AtomicInteger(); + + int tryAcquireSlot() { + for (;;) { + int acquired = get(); + if (acquired >= 2) { + return -1; + } + + if (compareAndSet(acquired, acquired + 1)) { + return acquired; + } + } + } + + boolean releaseSlot() { + return releaseIndex.incrementAndGet() == 2; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelFlatMapStream.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelFlatMapStream.java new file mode 100644 index 0000000000..beae3fee36 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelFlatMapStream.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.Stream; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.parallel.ParallelFlowable; + +/** + * Flattens the generated {@link Stream}s on each rail. + * + * @param the input value type + * @param the output value type + * @since 3.0.0 + */ +public final class ParallelFlatMapStream extends ParallelFlowable { + + final ParallelFlowable source; + + final Function> mapper; + + final int prefetch; + + public ParallelFlatMapStream( + ParallelFlowable source, + Function> mapper, + int prefetch) { + this.source = source; + this.mapper = mapper; + this.prefetch = prefetch; + } + + @Override + public int parallelism() { + return source.parallelism(); + } + + @Override + public void subscribe(Subscriber[] subscribers) { + if (!validate(subscribers)) { + return; + } + + int n = subscribers.length; + + @SuppressWarnings("unchecked") + final Subscriber[] parents = new Subscriber[n]; + + for (int i = 0; i < n; i++) { + parents[i] = FlowableFlatMapStream.subscribe(subscribers[i], mapper, prefetch); + } + + source.subscribe(parents); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapOptional.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapOptional.java new file mode 100644 index 0000000000..a31be9a75e --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapOptional.java @@ -0,0 +1,236 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Maps each 'rail' of the source ParallelFlowable with a mapper function. + * + * @param the input value type + * @param the output value type + * @since 3.0.0 + */ +public final class ParallelMapOptional extends ParallelFlowable { + + final ParallelFlowable source; + + final Function> mapper; + + public ParallelMapOptional(ParallelFlowable source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + public void subscribe(Subscriber[] subscribers) { + if (!validate(subscribers)) { + return; + } + + int n = subscribers.length; + @SuppressWarnings("unchecked") + Subscriber[] parents = new Subscriber[n]; + + for (int i = 0; i < n; i++) { + Subscriber a = subscribers[i]; + if (a instanceof ConditionalSubscriber) { + parents[i] = new ParallelMapConditionalSubscriber<>((ConditionalSubscriber)a, mapper); + } else { + parents[i] = new ParallelMapSubscriber<>(a, mapper); + } + } + + source.subscribe(parents); + } + + @Override + public int parallelism() { + return source.parallelism(); + } + + static final class ParallelMapSubscriber implements ConditionalSubscriber, Subscription { + + final Subscriber downstream; + + final Function> mapper; + + Subscription upstream; + + boolean done; + + ParallelMapSubscriber(Subscriber actual, Function> mapper) { + this.downstream = actual; + this.mapper = mapper; + } + + @Override + public void request(long n) { + upstream.request(n); + } + + @Override + public void cancel() { + upstream.cancel(); + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T t) { + if (!tryOnNext(t)) { + upstream.request(1); + } + } + + @Override + public boolean tryOnNext(T t) { + if (done) { + return true; + } + Optional v; + + try { + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Optional"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + cancel(); + onError(ex); + return true; + } + + if (v.isPresent()) { + downstream.onNext(v.get()); + return true; + } + return false; + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + return; + } + done = true; + downstream.onError(t); + } + + @Override + public void onComplete() { + if (done) { + return; + } + done = true; + downstream.onComplete(); + } + + } + static final class ParallelMapConditionalSubscriber implements ConditionalSubscriber, Subscription { + + final ConditionalSubscriber downstream; + + final Function> mapper; + + Subscription upstream; + + boolean done; + + ParallelMapConditionalSubscriber(ConditionalSubscriber actual, Function> mapper) { + this.downstream = actual; + this.mapper = mapper; + } + + @Override + public void request(long n) { + upstream.request(n); + } + + @Override + public void cancel() { + upstream.cancel(); + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T t) { + if (!tryOnNext(t)) { + upstream.request(1); + } + } + + @Override + public boolean tryOnNext(T t) { + if (done) { + return false; + } + Optional v; + + try { + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + cancel(); + onError(ex); + return false; + } + + return v.isPresent() && downstream.tryOnNext(v.get()); + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + return; + } + done = true; + downstream.onError(t); + } + + @Override + public void onComplete() { + if (done) { + return; + } + done = true; + downstream.onComplete(); + } + + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapTryOptional.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapTryOptional.java new file mode 100644 index 0000000000..d463c7bbab --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapTryOptional.java @@ -0,0 +1,306 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.parallel.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Maps each 'rail' of the source ParallelFlowable with a mapper function + * and handle any failure based on a handler function. + * @param the input value type + * @param the output value type + * @since 3.0.0 + */ +public final class ParallelMapTryOptional extends ParallelFlowable { + + final ParallelFlowable source; + + final Function> mapper; + + final BiFunction errorHandler; + + public ParallelMapTryOptional( + ParallelFlowable source, + Function> mapper, + BiFunction errorHandler) { + this.source = source; + this.mapper = mapper; + this.errorHandler = errorHandler; + } + + @Override + public void subscribe(Subscriber[] subscribers) { + if (!validate(subscribers)) { + return; + } + + int n = subscribers.length; + @SuppressWarnings("unchecked") + Subscriber[] parents = new Subscriber[n]; + + for (int i = 0; i < n; i++) { + Subscriber a = subscribers[i]; + if (a instanceof ConditionalSubscriber) { + parents[i] = new ParallelMapTryConditionalSubscriber<>((ConditionalSubscriber)a, mapper, errorHandler); + } else { + parents[i] = new ParallelMapTrySubscriber<>(a, mapper, errorHandler); + } + } + + source.subscribe(parents); + } + + @Override + public int parallelism() { + return source.parallelism(); + } + + static final class ParallelMapTrySubscriber implements ConditionalSubscriber, Subscription { + + final Subscriber downstream; + + final Function> mapper; + + final BiFunction errorHandler; + + Subscription upstream; + + boolean done; + + ParallelMapTrySubscriber(Subscriber actual, + Function> mapper, + BiFunction errorHandler) { + this.downstream = actual; + this.mapper = mapper; + this.errorHandler = errorHandler; + } + + @Override + public void request(long n) { + upstream.request(n); + } + + @Override + public void cancel() { + upstream.cancel(); + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T t) { + if (!tryOnNext(t) && !done) { + upstream.request(1); + } + } + + @Override + public boolean tryOnNext(T t) { + if (done) { + return false; + } + long retries = 0; + + for (;;) { + Optional v; + + try { + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Optional"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + + ParallelFailureHandling h; + + try { + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); + } catch (Throwable exc) { + Exceptions.throwIfFatal(exc); + cancel(); + onError(new CompositeException(ex, exc)); + return false; + } + + switch (h) { + case RETRY: + continue; + case SKIP: + return false; + case STOP: + cancel(); + onComplete(); + return false; + default: + cancel(); + onError(ex); + return false; + } + } + + if (v.isPresent()) { + downstream.onNext(v.get()); + return true; + } + return false; + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + return; + } + done = true; + downstream.onError(t); + } + + @Override + public void onComplete() { + if (done) { + return; + } + done = true; + downstream.onComplete(); + } + + } + static final class ParallelMapTryConditionalSubscriber implements ConditionalSubscriber, Subscription { + + final ConditionalSubscriber downstream; + + final Function> mapper; + + final BiFunction errorHandler; + + Subscription upstream; + + boolean done; + + ParallelMapTryConditionalSubscriber(ConditionalSubscriber actual, + Function> mapper, + BiFunction errorHandler) { + this.downstream = actual; + this.mapper = mapper; + this.errorHandler = errorHandler; + } + + @Override + public void request(long n) { + upstream.request(n); + } + + @Override + public void cancel() { + upstream.cancel(); + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T t) { + if (!tryOnNext(t) && !done) { + upstream.request(1); + } + } + + @Override + public boolean tryOnNext(T t) { + if (done) { + return false; + } + long retries = 0; + + for (;;) { + Optional v; + + try { + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Optional"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + + ParallelFailureHandling h; + + try { + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); + } catch (Throwable exc) { + Exceptions.throwIfFatal(exc); + cancel(); + onError(new CompositeException(ex, exc)); + return false; + } + + switch (h) { + case RETRY: + continue; + case SKIP: + return false; + case STOP: + cancel(); + onComplete(); + return false; + default: + cancel(); + onError(ex); + return false; + } + } + + return v.isPresent() && downstream.tryOnNext(v.get()); + } + } + + @Override + public void onError(Throwable t) { + if (done) { + RxJavaPlugins.onError(t); + return; + } + done = true; + downstream.onError(t); + } + + @Override + public void onComplete() { + if (done) { + return; + } + done = true; + downstream.onComplete(); + } + + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowable.java new file mode 100644 index 0000000000..8ed45d40b9 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowable.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.Stream; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.jdk8.MaybeFlattenStreamAsFlowable.FlattenStreamMultiObserver; + +/** + * Map the success value into a Java {@link Stream} and emits its values. + * + * @param the source value type + * @param the output value type + * @since 3.0.0 + */ +public final class SingleFlattenStreamAsFlowable extends Flowable { + + final Single source; + + final Function> mapper; + + public SingleFlattenStreamAsFlowable(Single source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(@NonNull Subscriber s) { + source.subscribe(new FlattenStreamMultiObserver<>(s, mapper)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsObservable.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsObservable.java new file mode 100644 index 0000000000..4733474f13 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsObservable.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.Stream; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.jdk8.MaybeFlattenStreamAsObservable.FlattenStreamMultiObserver; + +/** + * Map the success value into a Java {@link Stream} and emits its values. + * + * @param the source value type + * @param the output value type + * @since 3.0.0 + */ +public final class SingleFlattenStreamAsObservable extends Observable { + + final Single source; + + final Function> mapper; + + public SingleFlattenStreamAsObservable(Single source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(@NonNull Observer s) { + source.subscribe(new FlattenStreamMultiObserver<>(s, mapper)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFromCompletionStage.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFromCompletionStage.java new file mode 100644 index 0000000000..126a096a26 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleFromCompletionStage.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletionStage; +import java.util.function.BiConsumer; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.jdk8.FlowableFromCompletionStage.BiConsumerAtomicReference; + +/** + * Wrap a CompletionStage and signal its outcome. + * @param the element type + * @since 3.0.0 + */ +public final class SingleFromCompletionStage extends Single { + + final CompletionStage stage; + + public SingleFromCompletionStage(CompletionStage stage) { + this.stage = stage; + } + + @Override + protected void subscribeActual(SingleObserver observer) { + // We need an indirection because one can't detach from a whenComplete + // and cancellation should not hold onto the stage. + BiConsumerAtomicReference whenReference = new BiConsumerAtomicReference<>(); + CompletionStageHandler handler = new CompletionStageHandler<>(observer, whenReference); + whenReference.lazySet(handler); + + observer.onSubscribe(handler); + stage.whenComplete(whenReference); + } + + static final class CompletionStageHandler + implements Disposable, BiConsumer { + + final SingleObserver downstream; + + final BiConsumerAtomicReference whenReference; + + CompletionStageHandler(SingleObserver downstream, BiConsumerAtomicReference whenReference) { + this.downstream = downstream; + this.whenReference = whenReference; + } + + @Override + public void accept(T item, Throwable error) { + if (error != null) { + downstream.onError(error); + } + else if (item != null) { + downstream.onSuccess(item); + } else { + downstream.onError(new NullPointerException("The CompletionStage terminated with null.")); + } + } + + @Override + public void dispose() { + whenReference.set(null); + } + + @Override + public boolean isDisposed() { + return whenReference.get() == null; + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleMapOptional.java b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleMapOptional.java new file mode 100644 index 0000000000..ad54068d34 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/jdk8/SingleMapOptional.java @@ -0,0 +1,105 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Maps the success value to an {@link Optional} and emits its non-empty value or completes. + * + * @param the upstream success value type + * @param the result value type + * @since 3.0.0 + */ +public final class SingleMapOptional extends Maybe { + + final Single source; + + final Function> mapper; + + public SingleMapOptional(Single source, Function> mapper) { + this.source = source; + this.mapper = mapper; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new MapOptionalSingleObserver<>(observer, mapper)); + } + + static final class MapOptionalSingleObserver implements SingleObserver, Disposable { + + final MaybeObserver downstream; + + final Function> mapper; + + Disposable upstream; + + MapOptionalSingleObserver(MaybeObserver downstream, Function> mapper) { + this.downstream = downstream; + this.mapper = mapper; + } + + @Override + public void dispose() { + Disposable d = this.upstream; + this.upstream = DisposableHelper.DISPOSED; + d.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(T value) { + Optional v; + + try { + v = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null item"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + if (v.isPresent()) { + downstream.onSuccess(v.get()); + } else { + downstream.onComplete(); + } + } + + @Override + public void onError(Throwable e) { + downstream.onError(e); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java b/src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java new file mode 100644 index 0000000000..e5d89a4502 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/AbstractDisposableAutoRelease.java @@ -0,0 +1,125 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import java.util.concurrent.atomic.AtomicReference; + +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps lambda callbacks and when the upstream terminates or the observer gets disposed, + * removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

History: 0.18.0 @ RxJavaExtensions + * @since 3.1.0 + */ +abstract class AbstractDisposableAutoRelease +extends AtomicReference +implements Disposable, LambdaConsumerIntrospection { + + private static final long serialVersionUID = 8924480688481408726L; + + final AtomicReference composite; + + final Consumer onError; + + final Action onComplete; + + AbstractDisposableAutoRelease( + DisposableContainer composite, + Consumer onError, + Action onComplete + ) { + this.onError = onError; + this.onComplete = onComplete; + this.composite = new AtomicReference<>(composite); + } + + public final void onError(Throwable t) { + if (get() != DisposableHelper.DISPOSED) { + lazySet(DisposableHelper.DISPOSED); + try { + onError.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(new CompositeException(t, e)); + } + } else { + RxJavaPlugins.onError(t); + } + removeSelf(); + } + + public final void onComplete() { + if (get() != DisposableHelper.DISPOSED) { + lazySet(DisposableHelper.DISPOSED); + try { + onComplete.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + } + removeSelf(); + } + + @Override + public final void dispose() { + DisposableHelper.dispose(this); + removeSelf(); + } + + final void removeSelf() { + DisposableContainer c = composite.getAndSet(null); + if (c != null) { + c.delete(this); + } + } + + @Override + public final boolean isDisposed() { + return DisposableHelper.isDisposed(get()); + } + + public final void onSubscribe(Disposable d) { + DisposableHelper.setOnce(this, d); + } + + @Override + public final boolean hasCustomOnError() { + return onError != Functions.ON_ERROR_MISSING; + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserver.java index 0beef61824..6bd12f3940 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,7 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BasicIntQueueDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BasicIntQueueDisposable.java index 84a5c017b5..726db3ae96 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BasicIntQueueDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BasicIntQueueDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.rxjava3.internal.fuseable.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; /** * An abstract QueueDisposable implementation, extending an AtomicInteger, diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposable.java index fb6fc08366..0daaf520a5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,7 +13,7 @@ package io.reactivex.rxjava3.internal.observers; -import io.reactivex.rxjava3.internal.fuseable.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; /** * An abstract QueueDisposable implementation that defaults all diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BiConsumerSingleObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BiConsumerSingleObserver.java index 2fb0221f64..1e9696cb15 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BiConsumerSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BiConsumerSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingBaseObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingBaseObserver.java index 9a128799d5..93ce06eedf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingBaseObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingBaseObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.observers; import java.util.concurrent.CountDownLatch; diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingDisposableMultiObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingDisposableMultiObserver.java new file mode 100644 index 0000000000..a2334c21ba --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingDisposableMultiObserver.java @@ -0,0 +1,154 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import java.util.concurrent.CountDownLatch; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.*; +import io.reactivex.rxjava3.internal.util.BlockingHelper; + +/** + * Blocks until the upstream terminates and dispatches the outcome to + * the actual observer. + * + * @param the element type of the source + * @since 3.0.0 + */ +public final class BlockingDisposableMultiObserver +extends CountDownLatch +implements MaybeObserver, SingleObserver, CompletableObserver, Disposable { + + T value; + Throwable error; + + final SequentialDisposable upstream; + + public BlockingDisposableMultiObserver() { + super(1); + upstream = new SequentialDisposable(); + } + + @Override + public void dispose() { + upstream.dispose(); + countDown(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + DisposableHelper.setOnce(upstream, d); + } + + @Override + public void onSuccess(@NonNull T t) { + this.value = t; + upstream.lazySet(Disposable.disposed()); + countDown(); + } + + @Override + public void onError(@NonNull Throwable e) { + this.error = e; + upstream.lazySet(Disposable.disposed()); + countDown(); + } + + @Override + public void onComplete() { + upstream.lazySet(Disposable.disposed()); + countDown(); + } + + public void blockingConsume(CompletableObserver observer) { + if (getCount() != 0) { + try { + BlockingHelper.verifyNonBlocking(); + await(); + } catch (InterruptedException ex) { + dispose(); + observer.onError(ex); + return; + } + } + if (isDisposed()) { + return; + } + + Throwable ex = error; + if (ex != null) { + observer.onError(ex); + } else { + observer.onComplete(); + } + } + + public void blockingConsume(SingleObserver observer) { + if (getCount() != 0) { + try { + BlockingHelper.verifyNonBlocking(); + await(); + } catch (InterruptedException ex) { + dispose(); + observer.onError(ex); + return; + } + } + if (isDisposed()) { + return; + } + + Throwable ex = error; + if (ex != null) { + observer.onError(ex); + } else { + observer.onSuccess(value); + } + } + + public void blockingConsume(MaybeObserver observer) { + if (getCount() != 0) { + try { + BlockingHelper.verifyNonBlocking(); + await(); + } catch (InterruptedException ex) { + dispose(); + observer.onError(ex); + return; + } + } + if (isDisposed()) { + return; + } + + Throwable ex = error; + if (ex != null) { + observer.onError(ex); + } else { + T v = value; + if (v == null) { + observer.onComplete(); + } else { + observer.onSuccess(v); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserver.java index 276848afe4..570000a55a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingLastObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingLastObserver.java index bf745d3ed1..c8e979cc31 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingLastObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingLastObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserver.java index 1f4ee305bd..458e6db5a1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * A combined Observer that awaits the success or error signal via a CountDownLatch. @@ -143,4 +146,39 @@ public boolean blockingAwait(long timeout, TimeUnit unit) { } return true; } + + /** + * Blocks until the source completes and calls the appropriate callback. + * @param onSuccess for a succeeding source + * @param onError for a failing source + * @param onComplete for an empty source + */ + public void blockingConsume(Consumer onSuccess, Consumer onError, Action onComplete) { + try { + if (getCount() != 0) { + try { + BlockingHelper.verifyNonBlocking(); + await(); + } catch (InterruptedException ex) { + dispose(); + onError.accept(ex); + return; + } + } + Throwable ex = error; + if (ex != null) { + onError.accept(ex); + return; + } + T v = value; + if (v != null) { + onSuccess.accept(v); + } else { + onComplete.run(); + } + } catch (Throwable t) { + Exceptions.throwIfFatal(t); + RxJavaPlugins.onError(t); + } + } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingObserver.java index da38180751..ff84f2a4c6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/BlockingObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserver.java index f6e7fed1cd..be2da69160 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,33 +20,24 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class CallbackCompletableObserver extends AtomicReference - implements CompletableObserver, Disposable, Consumer, LambdaConsumerIntrospection { + implements CompletableObserver, Disposable, LambdaConsumerIntrospection { private static final long serialVersionUID = -4361286194466301354L; final Consumer onError; final Action onComplete; - public CallbackCompletableObserver(Action onComplete) { - this.onError = this; - this.onComplete = onComplete; - } - public CallbackCompletableObserver(Consumer onError, Action onComplete) { this.onError = onError; this.onComplete = onComplete; } - @Override - public void accept(Throwable e) { - RxJavaPlugins.onError(new OnErrorNotImplementedException(e)); - } - @Override public void onComplete() { try { @@ -86,6 +77,6 @@ public boolean isDisposed() { @Override public boolean hasCustomOnError() { - return onError != this; + return onError != Functions.ON_ERROR_MISSING; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserver.java index b29a74e4bb..eff6e4a0c1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposable.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposable.java index 8db129efb6..084ad68571 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -114,7 +114,7 @@ public final void complete() { @Nullable @Override - public final T poll() throws Exception { + public final T poll() { if (get() == FUSED_READY) { T v = value; value = null; diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserver.java index a99fb49a57..4453e0ffa8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java new file mode 100644 index 0000000000..4779ff5723 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseMultiObserver.java @@ -0,0 +1,78 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.DisposableContainer; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps lambda callbacks and when the upstream terminates or this (Single | Maybe | Completable) + * observer gets disposed, removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

History: 0.18.0 @ RxJavaExtensions + * @param the element type consumed + * @since 3.1.0 + */ +public final class DisposableAutoReleaseMultiObserver +extends AbstractDisposableAutoRelease +implements SingleObserver, MaybeObserver, CompletableObserver { + + private static final long serialVersionUID = 8924480688481408726L; + + final Consumer onSuccess; + + public DisposableAutoReleaseMultiObserver( + DisposableContainer composite, + Consumer onSuccess, + Consumer onError, + Action onComplete + ) { + super(composite, onError, onComplete); + this.onSuccess = onSuccess; + } + + @Override + public void onSuccess(T t) { + if (get() != DisposableHelper.DISPOSED) { + lazySet(DisposableHelper.DISPOSED); + try { + onSuccess.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + } + removeSelf(); + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java new file mode 100644 index 0000000000..89435c96ed --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableAutoReleaseObserver.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.DisposableContainer; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Wraps lambda callbacks and when the upstream terminates or this observer gets disposed, + * removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

History: 0.18.0 @ RxJavaExtensions + * @param the element type consumed + * @since 3.1.0 + */ +public final class DisposableAutoReleaseObserver +extends AbstractDisposableAutoRelease +implements Observer { + + private static final long serialVersionUID = 8924480688481408726L; + + final Consumer onNext; + + public DisposableAutoReleaseObserver( + DisposableContainer composite, + Consumer onNext, + Consumer onError, + Action onComplete + ) { + super(composite, onError, onComplete); + this.onNext = onNext; + } + + @Override + public void onNext(T t) { + if (get() != DisposableHelper.DISPOSED) { + try { + onNext.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + get().dispose(); + onError(e); + } + } + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserver.java index 2e935140ca..529d651c67 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserver.java index 36a08269c8..e92893b9bd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/ForEachWhileObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/ForEachWhileObserver.java index 7017239ae0..bed185523f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/ForEachWhileObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/ForEachWhileObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserver.java similarity index 84% rename from src/main/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserver.java rename to src/main/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserver.java index 80abb70419..f109aaa5a3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,8 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; -import io.reactivex.rxjava3.core.SingleObserver; +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; import io.reactivex.rxjava3.internal.util.BlockingHelper; @@ -30,17 +31,17 @@ * * @param the value type */ -public final class FutureSingleObserver extends CountDownLatch -implements SingleObserver, Future, Disposable { +public final class FutureMultiObserver extends CountDownLatch +implements MaybeObserver, SingleObserver, CompletableObserver, Future, Disposable { T value; Throwable error; final AtomicReference upstream; - public FutureSingleObserver() { + public FutureMultiObserver() { super(1); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override @@ -89,7 +90,7 @@ public T get() throws InterruptedException, ExecutionException { } @Override - public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + public T get(long timeout, @NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (getCount() != 0) { BlockingHelper.verifyNonBlocking(); if (!await(timeout, unit)) { @@ -140,6 +141,16 @@ public void onError(Throwable t) { } } + @Override + public void onComplete() { + Disposable a = upstream.get(); + if (a == DisposableHelper.DISPOSED) { + return; + } + upstream.compareAndSet(a, this); + countDown(); + } + @Override public void dispose() { // ignoring as `this` means a finished Disposable only diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java index f3d067f7ca..13fd2dc9bf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/FutureObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,6 +19,7 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; @@ -41,7 +42,7 @@ public final class FutureObserver extends CountDownLatch public FutureObserver() { super(1); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override @@ -90,7 +91,7 @@ public T get() throws InterruptedException, ExecutionException { } @Override - public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + public T get(long timeout, @NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (getCount() != 0) { BlockingHelper.verifyNonBlocking(); if (!await(timeout, unit)) { @@ -127,22 +128,15 @@ public void onNext(T t) { @Override public void onError(Throwable t) { if (error == null) { - error = t; - - for (;;) { - Disposable a = upstream.get(); - if (a == this || a == DisposableHelper.DISPOSED) { - RxJavaPlugins.onError(t); - return; - } - if (upstream.compareAndSet(a, this)) { - countDown(); - return; - } + Disposable a = upstream.get(); + if (a != this && a != DisposableHelper.DISPOSED + && upstream.compareAndSet(a, this)) { + error = t; + countDown(); + return; } - } else { - RxJavaPlugins.onError(t); } + RxJavaPlugins.onError(t); } @Override @@ -151,15 +145,12 @@ public void onComplete() { onError(new NoSuchElementException("The source is empty")); return; } - for (;;) { - Disposable a = upstream.get(); - if (a == this || a == DisposableHelper.DISPOSED) { - return; - } - if (upstream.compareAndSet(a, this)) { - countDown(); - return; - } + Disposable a = upstream.get(); + if (a == this || a == DisposableHelper.DISPOSED) { + return; + } + if (upstream.compareAndSet(a, this)) { + countDown(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java index db89a98f6e..9d6417c0e8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,8 +18,10 @@ import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.QueueDrainHelper; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Subscriber that can fuse with the upstream and calls a support interface @@ -114,8 +116,4 @@ public void setDone() { public SimpleQueue queue() { return queue; } - - public int fusionMode() { - return fusionMode; - } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverSupport.java b/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverSupport.java index ff28bd092c..01bebaf007 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverSupport.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverSupport.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/LambdaObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/LambdaObserver.java index c52017fa4d..6bd5dece94 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/LambdaObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/LambdaObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java index 67bc900d17..aa25f4286c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,8 +17,8 @@ import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; /** * Abstract base class for subscribers that hold another subscriber, a queue @@ -57,10 +57,6 @@ public final boolean enter() { return wip.getAndIncrement() == 0; } - public final boolean fastEnter() { - return wip.get() == 0 && wip.compareAndSet(0, 1); - } - protected final void fastPathEmit(U value, boolean delayError, Disposable dispose) { final Observer observer = downstream; final SimplePlainQueue q = queue; diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/ResumeSingleObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/ResumeSingleObserver.java index 90a29bef1c..acaa71257c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/ResumeSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/ResumeSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/SafeCompletableObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/SafeCompletableObserver.java new file mode 100644 index 0000000000..18e16383af --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/SafeCompletableObserver.java @@ -0,0 +1,78 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.CompletableObserver; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps another {@link CompletableObserver} and catches exceptions thrown by its + * {@code onSubscribe}, {@code onError} or + * {@code onComplete} methods despite the protocol forbids it. + *

+ * Such exceptions are routed to the {@link RxJavaPlugins#onError(Throwable)} handler. + * + * @since 3.0.0 + */ +public final class SafeCompletableObserver implements CompletableObserver { + + final CompletableObserver downstream; + + boolean onSubscribeFailed; + + public SafeCompletableObserver(CompletableObserver downstream) { + this.downstream = downstream; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + try { + downstream.onSubscribe(d); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + onSubscribeFailed = true; + d.dispose(); + RxJavaPlugins.onError(ex); + } + } + + @Override + public void onError(@NonNull Throwable e) { + if (onSubscribeFailed) { + RxJavaPlugins.onError(e); + } else { + try { + downstream.onError(e); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(new CompositeException(e, ex)); + } + } + } + + @Override + public void onComplete() { + if (!onSubscribeFailed) { + try { + downstream.onComplete(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/SafeMaybeObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/SafeMaybeObserver.java new file mode 100644 index 0000000000..320ac53427 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/SafeMaybeObserver.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.MaybeObserver; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps another {@link MaybeObserver} and catches exceptions thrown by its + * {@code onSubscribe}, {@code onSuccess}, {@code onError} or + * {@code onComplete} methods despite the protocol forbids it. + *

+ * Such exceptions are routed to the {@link RxJavaPlugins#onError(Throwable)} handler. + * + * @param the element type of the sequence + * @since 3.0.0 + */ +public final class SafeMaybeObserver implements MaybeObserver { + + final MaybeObserver downstream; + + boolean onSubscribeFailed; + + public SafeMaybeObserver(MaybeObserver downstream) { + this.downstream = downstream; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + try { + downstream.onSubscribe(d); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + onSubscribeFailed = true; + d.dispose(); + RxJavaPlugins.onError(ex); + } + } + + @Override + public void onSuccess(@NonNull T t) { + if (!onSubscribeFailed) { + try { + downstream.onSuccess(t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + } + + @Override + public void onError(@NonNull Throwable e) { + if (onSubscribeFailed) { + RxJavaPlugins.onError(e); + } else { + try { + downstream.onError(e); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(new CompositeException(e, ex)); + } + } + } + + @Override + public void onComplete() { + if (!onSubscribeFailed) { + try { + downstream.onComplete(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/SafeSingleObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/SafeSingleObserver.java new file mode 100644 index 0000000000..3e73d1e639 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/observers/SafeSingleObserver.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.SingleObserver; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps another {@link SingleObserver} and catches exceptions thrown by its + * {@code onSubscribe}, {@code onSuccess} or {@code onError} methods despite + * the protocol forbids it. + *

+ * Such exceptions are routed to the {@link RxJavaPlugins#onError(Throwable)} handler. + * + * @param the element type of the sequence + * @since 3.0.0 + */ +public final class SafeSingleObserver implements SingleObserver { + + final SingleObserver downstream; + + boolean onSubscribeFailed; + + public SafeSingleObserver(SingleObserver downstream) { + this.downstream = downstream; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + try { + downstream.onSubscribe(d); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + onSubscribeFailed = true; + d.dispose(); + RxJavaPlugins.onError(ex); + } + } + + @Override + public void onSuccess(@NonNull T t) { + if (!onSubscribeFailed) { + try { + downstream.onSuccess(t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + } + + @Override + public void onError(@NonNull Throwable e) { + if (onSubscribeFailed) { + RxJavaPlugins.onError(e); + } else { + try { + downstream.onError(e); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(new CompositeException(e, ex)); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/observers/SubscriberCompletableObserver.java b/src/main/java/io/reactivex/rxjava3/internal/observers/SubscriberCompletableObserver.java deleted file mode 100644 index 2bbc5128d6..0000000000 --- a/src/main/java/io/reactivex/rxjava3/internal/observers/SubscriberCompletableObserver.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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. - */ - -package io.reactivex.rxjava3.internal.observers; - -import org.reactivestreams.*; - -import io.reactivex.rxjava3.core.CompletableObserver; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.disposables.DisposableHelper; - -public final class SubscriberCompletableObserver implements CompletableObserver, Subscription { - final Subscriber subscriber; - - Disposable upstream; - - public SubscriberCompletableObserver(Subscriber subscriber) { - this.subscriber = subscriber; - } - - @Override - public void onComplete() { - subscriber.onComplete(); - } - - @Override - public void onError(Throwable e) { - subscriber.onError(e); - } - - @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(this.upstream, d)) { - this.upstream = d; - - subscriber.onSubscribe(this); - } - } - - @Override - public void request(long n) { - // ignored, no values emitted anyway - } - - @Override - public void cancel() { - upstream.dispose(); - } -} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmb.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmb.java index 14e8ae464b..60fa3ce4c6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmb.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmb.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletable.java index d006329aea..13e582e764 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCache.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCache.java index 253e518dad..c5fd3279d6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCache.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCache.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public final class CompletableCache extends Completable implements CompletableOb public CompletableCache(CompletableSource source) { this.source = source; - this.observers = new AtomicReference(EMPTY); + this.observers = new AtomicReference<>(EMPTY); this.once = new AtomicBoolean(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java index 9d55b626ea..00a53f3ad7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcat.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,9 +21,8 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class CompletableConcat extends Completable { @@ -106,9 +105,9 @@ public void onSubscribe(Subscription s) { } if (prefetch == Integer.MAX_VALUE) { - queue = new SpscLinkedArrayQueue(Flowable.bufferSize()); + queue = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); } else { - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); } downstream.onSubscribe(this); @@ -121,7 +120,7 @@ public void onSubscribe(Subscription s) { public void onNext(CompletableSource t) { if (sourceFused == QueueSubscription.NONE) { if (!queue.offer(t)) { - onError(new MissingBackpressureException()); + onError(new QueueOverflowException()); return; } } @@ -182,9 +181,8 @@ void drain() { boolean empty = cs == null; if (d && empty) { - if (once.compareAndSet(false, true)) { - downstream.onComplete(); - } + // errors never set done or call drain. + downstream.onComplete(); return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArray.java index c423c37587..e87968e774 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatIterable.java index c36f546c16..73510018ff 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.completable; import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; public final class CompletableConcatIterable extends Completable { final Iterable sources; @@ -35,7 +35,7 @@ public void subscribeActual(CompletableObserver observer) { Iterator it; try { - it = ObjectHelper.requireNonNull(sources.iterator(), "The iterator returned is null"); + it = Objects.requireNonNull(sources.iterator(), "The iterator returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptyDisposable.error(e, observer); @@ -109,7 +109,7 @@ void next() { CompletableSource c; try { - c = ObjectHelper.requireNonNull(a.next(), "The CompletableSource returned is null"); + c = Objects.requireNonNull(a.next(), "The CompletableSource returned is null"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreate.java index 4004630756..b721cbb351 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDefer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDefer.java index 0e28d0d28f..2a7763bb52 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDefer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDefer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class CompletableDefer extends Completable { @@ -32,7 +33,7 @@ protected void subscribeActual(CompletableObserver observer) { CompletableSource c; try { - c = ObjectHelper.requireNonNull(completableSupplier.get(), "The completableSupplier returned a null CompletableSource"); + c = Objects.requireNonNull(completableSupplier.get(), "The completableSupplier returned a null CompletableSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptyDisposable.error(e, observer); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelay.java index 49c25bf6e1..c21134569b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetach.java index efd28228fa..d01d297495 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOn.java index 16f9fa159c..bd213b5fc4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinally.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinally.java index 425ed4caea..02fa08cdb0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinally.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinally.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnEvent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnEvent.java index 63a408efc4..dfa24b000f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnEvent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnEvent.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableEmpty.java index e18ccc9421..7c18a4ddc9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableError.java index 70d4e41006..8d2cb2fcc2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableErrorSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableErrorSupplier.java index 12b39277db..80c95806b8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableErrorSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableErrorSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class CompletableErrorSupplier extends Completable { @@ -32,7 +33,7 @@ protected void subscribeActual(CompletableObserver observer) { Throwable error; try { - error = ObjectHelper.requireNonNull(errorSupplier.get(), "The error returned is null"); + error = Objects.requireNonNull(errorSupplier.get(), "The error returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); error = e; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromAction.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromAction.java index 5ce4d43547..53cfc2615b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromAction.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromAction.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,21 +29,23 @@ public CompletableFromAction(Action run) { @Override protected void subscribeActual(CompletableObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); - try { - run.run(); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); + if (!d.isDisposed()) { + try { + run.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + if (!d.isDisposed()) { + observer.onError(e); + } else { + RxJavaPlugins.onError(e); + } + return; + } if (!d.isDisposed()) { - observer.onError(e); - } else { - RxJavaPlugins.onError(e); + observer.onComplete(); } - return; - } - if (!d.isDisposed()) { - observer.onComplete(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallable.java index ac2c94c921..90f9266934 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public CompletableFromCallable(Callable callable) { @Override protected void subscribeActual(CompletableObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); try { callable.call(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservable.java index d368a04244..25dffbbebf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public CompletableFromObservable(ObservableSource observable) { @Override protected void subscribeActual(final CompletableObserver observer) { - observable.subscribe(new CompletableFromObservableObserver(observer)); + observable.subscribe(new CompletableFromObservableObserver<>(observer)); } static final class CompletableFromObservableObserver implements Observer { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisher.java index 0e65777e3f..4205d1141c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public CompletableFromPublisher(Publisher flowable) { @Override protected void subscribeActual(final CompletableObserver downstream) { - flowable.subscribe(new FromPublisherSubscriber(downstream)); + flowable.subscribe(new FromPublisherSubscriber<>(downstream)); } static final class FromPublisherSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnable.java index 3dc275e0e1..79f2efb470 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,21 +28,23 @@ public CompletableFromRunnable(Runnable runnable) { @Override protected void subscribeActual(CompletableObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); - try { - runnable.run(); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); + if (!d.isDisposed()) { + try { + runnable.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + if (!d.isDisposed()) { + observer.onError(e); + } else { + RxJavaPlugins.onError(e); + } + return; + } if (!d.isDisposed()) { - observer.onError(e); - } else { - RxJavaPlugins.onError(e); + observer.onComplete(); } - return; - } - if (!d.isDisposed()) { - observer.onComplete(); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingle.java index adee6c86f0..d55fc30339 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public CompletableFromSingle(SingleSource single) { @Override protected void subscribeActual(final CompletableObserver observer) { - single.subscribe(new CompletableFromSingleObserver(observer)); + single.subscribe(new CompletableFromSingleObserver<>(observer)); } static final class CompletableFromSingleObserver implements SingleObserver { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplier.java index 5c6054306a..8b45e6b5f0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public CompletableFromSupplier(Supplier supplier) { @Override protected void subscribeActual(CompletableObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); try { supplier.get(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromUnsafeSource.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromUnsafeSource.java index 02857d4692..dfc0356bf7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromUnsafeSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromUnsafeSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHide.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHide.java index 16708ee90e..536855a7b7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHide.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHide.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLift.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLift.java index 58356a73dd..6749689bcc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLift.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLift.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterialize.java index 129084898a..198ba94b1a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,6 +34,6 @@ public CompletableMaterialize(Completable source) { @Override protected void subscribeActual(SingleObserver> observer) { - source.subscribe(new MaterializeSingleObserver(observer)); + source.subscribe(new MaterializeSingleObserver<>(observer)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMerge.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMerge.java index 9d916d84b6..2528fe2f3b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMerge.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMerge.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java index ec9c675210..56095221cc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -86,9 +86,8 @@ public void onError(Throwable e) { @Override public void onComplete() { if (decrementAndGet() == 0) { - if (once.compareAndSet(false, true)) { - downstream.onComplete(); - } + // errors don't decrement this + downstream.onComplete(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArrayDelayError.java similarity index 96% rename from src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorArray.java rename to src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArrayDelayError.java index 549fd10a48..85b4c61225 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeArrayDelayError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,11 +19,11 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; -public final class CompletableMergeDelayErrorArray extends Completable { +public final class CompletableMergeArrayDelayError extends Completable { final CompletableSource[] sources; - public CompletableMergeDelayErrorArray(CompletableSource[] sources) { + public CompletableMergeArrayDelayError(CompletableSource[] sources) { this.sources = sources; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java index 6d420cf044..cb62974753 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeDelayErrorIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.completable; import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.operators.completable.CompletableMergeDelayErrorArray.*; +import io.reactivex.rxjava3.internal.operators.completable.CompletableMergeArrayDelayError.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; public final class CompletableMergeDelayErrorIterable extends Completable { @@ -40,7 +40,7 @@ public void subscribeActual(final CompletableObserver observer) { Iterator iterator; try { - iterator = ObjectHelper.requireNonNull(sources.iterator(), "The source iterator returned is null"); + iterator = Objects.requireNonNull(sources.iterator(), "The source iterator returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); observer.onError(e); @@ -77,7 +77,7 @@ public void subscribeActual(final CompletableObserver observer) { CompletableSource c; try { - c = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null CompletableSource"); + c = Objects.requireNonNull(iterator.next(), "The iterator returned a null CompletableSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); errors.tryAddThrowableOrReport(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java index 86c08d359c..3f2dce90e1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,12 +14,12 @@ package io.reactivex.rxjava3.internal.operators.completable; import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class CompletableMergeIterable extends Completable { @@ -41,7 +41,7 @@ public void subscribeActual(final CompletableObserver observer) { Iterator iterator; try { - iterator = ObjectHelper.requireNonNull(sources.iterator(), "The source iterator returned is null"); + iterator = Objects.requireNonNull(sources.iterator(), "The source iterator returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); observer.onError(e); @@ -74,7 +74,7 @@ public void subscribeActual(final CompletableObserver observer) { CompletableSource c; try { - c = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null CompletableSource"); + c = Objects.requireNonNull(iterator.next(), "The iterator returned a null CompletableSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); set.dispose(); @@ -128,9 +128,8 @@ public void onError(Throwable e) { @Override public void onComplete() { if (wip.decrementAndGet() == 0) { - if (compareAndSet(false, true)) { - downstream.onComplete(); - } + // errors don't decrement wip + downstream.onComplete(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableNever.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableNever.java index 8c11621a7c..17bc2cc140 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableNever.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableNever.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOn.java index 8bccc8ced0..b48ade189f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorComplete.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorComplete.java index 6d87a4275e..c4252e7f3b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorComplete.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorComplete.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,15 +32,18 @@ public CompletableOnErrorComplete(CompletableSource source, Predicate predicate; - OnError(CompletableObserver observer) { + OnError(CompletableObserver observer, + Predicate predicate) { this.downstream = observer; + this.predicate = predicate; } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorReturn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorReturn.java new file mode 100644 index 0000000000..9cd91262a5 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorReturn.java @@ -0,0 +1,99 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +import java.util.Objects; + +/** + * Returns a value generated via a function if the main source signals an onError. + * @param the value type + * @since 3.0.0 + */ +public final class CompletableOnErrorReturn extends Maybe { + + final CompletableSource source; + + final Function valueSupplier; + + public CompletableOnErrorReturn(CompletableSource source, + Function valueSupplier) { + this.source = source; + this.valueSupplier = valueSupplier; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new OnErrorReturnMaybeObserver<>(observer, valueSupplier)); + } + + static final class OnErrorReturnMaybeObserver implements CompletableObserver, Disposable { + + final MaybeObserver downstream; + + final Function itemSupplier; + + Disposable upstream; + + OnErrorReturnMaybeObserver(MaybeObserver actual, + Function itemSupplier) { + this.downstream = actual; + this.itemSupplier = itemSupplier; + } + + @Override + public void dispose() { + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onError(Throwable e) { + T v; + + try { + v = Objects.requireNonNull(itemSupplier.apply(e), "The itemSupplier returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(new CompositeException(e, ex)); + return; + } + + downstream.onSuccess(v); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeek.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeek.java index 04955c6931..aeba5381b5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeek.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeek.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNext.java index 63227fb391..785501511a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.completable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; public final class CompletableResumeNext extends Completable { @@ -79,7 +79,7 @@ public void onError(Throwable e) { CompletableSource c; try { - c = ObjectHelper.requireNonNull(errorMapper.apply(e), "The errorMapper returned a null CompletableSource"); + c = Objects.requireNonNull(errorMapper.apply(e), "The errorMapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(new CompositeException(e, ex)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOn.java index f76ac93595..748602f978 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilCompletable.java index bd7e519a17..364484ab10 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeout.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeout.java index 1a38bb383a..80f40dfd72 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeout.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeout.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimer.java index 4e271af773..b50fd5db2e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowable.java index ccbafd06b5..346267ce13 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.observers.SubscriberCompletableObserver; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableFromCompletable; public final class CompletableToFlowable extends Flowable { @@ -28,7 +28,6 @@ public CompletableToFlowable(CompletableSource source) { @Override protected void subscribeActual(Subscriber s) { - SubscriberCompletableObserver os = new SubscriberCompletableObserver(s); - source.subscribe(os); + source.subscribe(new FlowableFromCompletable.FromCompletableObserver<>(s)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservable.java index 91ce597c3b..ccf1a9e504 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,9 +14,7 @@ package io.reactivex.rxjava3.internal.operators.completable; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.observers.BasicQueueDisposable; +import io.reactivex.rxjava3.internal.operators.observable.ObservableFromCompletable; /** * Wraps a Completable and exposes it as an Observable. @@ -33,66 +31,6 @@ public CompletableToObservable(CompletableSource source) { @Override protected void subscribeActual(Observer observer) { - source.subscribe(new ObserverCompletableObserver(observer)); - } - - static final class ObserverCompletableObserver extends BasicQueueDisposable - implements CompletableObserver { - - final Observer observer; - - Disposable upstream; - - ObserverCompletableObserver(Observer observer) { - this.observer = observer; - } - - @Override - public void onComplete() { - observer.onComplete(); - } - - @Override - public void onError(Throwable e) { - observer.onError(e); - } - - @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - this.upstream = d; - observer.onSubscribe(this); - } - } - - @Override - public int requestFusion(int mode) { - return mode & ASYNC; - } - - @Override - public Void poll() throws Exception { - return null; // always empty - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public void clear() { - // always empty - } - - @Override - public void dispose() { - upstream.dispose(); - } - - @Override - public boolean isDisposed() { - return upstream.isDisposed(); - } + source.subscribe(new ObservableFromCompletable.FromCompletableObserver<>(observer)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToSingle.java index c35d909796..58e47247df 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsing.java index ab29ce4ae8..648e6529e2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsing.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.completable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class CompletableUsing extends Completable { @@ -54,7 +54,7 @@ protected void subscribeActual(CompletableObserver observer) { CompletableSource source; try { - source = ObjectHelper.requireNonNull(completableFunction.apply(resource), "The completableFunction returned a null CompletableSource"); + source = Objects.requireNonNull(completableFunction.apply(resource), "The completableFunction returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (eager) { @@ -80,7 +80,7 @@ protected void subscribeActual(CompletableObserver observer) { return; } - source.subscribe(new UsingObserver(observer, resource, disposer, eager)); + source.subscribe(new UsingObserver<>(observer, resource, disposer, eager)); } static final class UsingObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java new file mode 100644 index 0000000000..62c7d07a88 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber.java @@ -0,0 +1,165 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Abstract base class for operators that throttle excessive updates from upstream in case if + * downstream {@link Subscriber} is not ready to receive updates. + * + * @param the upstream value type + * @param the downstream value type + */ +abstract class AbstractBackpressureThrottlingSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { + + private static final long serialVersionUID = -5050301752721603566L; + + final Subscriber downstream; + + Subscription upstream; + + volatile boolean done; + Throwable error; + + volatile boolean cancelled; + + final AtomicLong requested = new AtomicLong(); + + final AtomicReference current = new AtomicReference<>(); + + AbstractBackpressureThrottlingSubscriber(Subscriber downstream) { + this.downstream = downstream; + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + downstream.onSubscribe(this); + s.request(Long.MAX_VALUE); + } + } + + @Override + public abstract void onNext(T t); + + @Override + public void onError(Throwable t) { + error = t; + done = true; + drain(); + } + + @Override + public void onComplete() { + done = true; + drain(); + } + + @Override + public void request(long n) { + if (SubscriptionHelper.validate(n)) { + BackpressureHelper.add(requested, n); + drain(); + } + } + + @Override + public void cancel() { + if (!cancelled) { + cancelled = true; + upstream.cancel(); + + if (getAndIncrement() == 0) { + current.lazySet(null); + } + } + } + + void drain() { + if (getAndIncrement() != 0) { + return; + } + final Subscriber a = downstream; + int missed = 1; + final AtomicLong r = requested; + final AtomicReference q = current; + + for (;;) { + long e = 0L; + + while (e != r.get()) { + boolean d = done; + R v = q.getAndSet(null); + boolean empty = v == null; + + if (checkTerminated(d, empty, a, q)) { + return; + } + + if (empty) { + break; + } + + a.onNext(v); + + e++; + } + + if (e == r.get() && checkTerminated(done, q.get() == null, a, q)) { + return; + } + + if (e != 0L) { + BackpressureHelper.produced(r, e); + } + + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } + } + + boolean checkTerminated(boolean d, boolean empty, Subscriber a, AtomicReference q) { + if (cancelled) { + q.lazySet(null); + return true; + } + + if (d) { + Throwable e = error; + if (e != null) { + q.lazySet(null); + a.onError(e); + return true; + } else + if (empty) { + a.onComplete(); + return true; + } + } + + return false; + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstream.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstream.java index 76a68d37b3..4a9484f743 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstream.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstream.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,9 +16,10 @@ import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.HasUpstreamPublisher; +import java.util.Objects; + /** * Abstract base class for operators that take an upstream * source {@link Publisher}. @@ -39,7 +40,7 @@ abstract class AbstractFlowableWithUpstream extends Flowable implements * @param source the source (upstream) Publisher instance, not null (verified) */ AbstractFlowableWithUpstream(Flowable source) { - this.source = ObjectHelper.requireNonNull(source, "source is null"); + this.source = Objects.requireNonNull(source, "source is null"); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableIterable.java index 151541f63f..11239d04a7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,9 +22,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscArrayQueue; public final class BlockingFlowableIterable implements Iterable { final Flowable source; @@ -38,7 +38,7 @@ public BlockingFlowableIterable(Flowable source, int bufferSize) { @Override public Iterator iterator() { - BlockingFlowableIterator it = new BlockingFlowableIterator(bufferSize); + BlockingFlowableIterator it = new BlockingFlowableIterator<>(bufferSize); source.subscribe(it); return it; } @@ -65,7 +65,7 @@ static final class BlockingFlowableIterator volatile Throwable error; BlockingFlowableIterator(int batchSize) { - this.queue = new SpscArrayQueue(batchSize); + this.queue = new SpscArrayQueue<>(batchSize); this.batchSize = batchSize; this.limit = batchSize - (batchSize >> 2); this.lock = new ReentrantLock(); @@ -138,9 +138,12 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { if (!queue.offer(t)) { + // Error must be set first before calling cancel to avoid race + // with hasNext(), which checks for cancel first before checking + // for error. + error = new QueueOverflowException(); SubscriptionHelper.cancel(this); - - onError(new MissingBackpressureException("Queue full?!")); + onComplete(); } else { signalConsumer(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatest.java index 91fce3e427..7628776ae0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public BlockingFlowableLatest(Publisher source) { @Override public Iterator iterator() { - LatestSubscriberIterator lio = new LatestSubscriberIterator(); + LatestSubscriberIterator lio = new LatestSubscriberIterator<>(); Flowable.fromPublisher(source).materialize().subscribe(lio); return lio; } @@ -48,7 +48,7 @@ public Iterator iterator() { static final class LatestSubscriberIterator extends DisposableSubscriber> implements Iterator { final Semaphore notify = new Semaphore(0); // observer's notification - final AtomicReference> value = new AtomicReference>(); + final AtomicReference> value = new AtomicReference<>(); // iterator's notification Notification iteratorNotification; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java index f8fae1d413..7c41634c55 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecent.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ * Returns an Iterable that always returns the item most recently emitted by an Observable, or a * seed value if no item has yet been emitted. *

- * + * * * @param the value type */ @@ -40,7 +40,7 @@ public BlockingFlowableMostRecent(Flowable source, T initialValue) { @Override public Iterator iterator() { - MostRecentSubscriber mostRecentSubscriber = new MostRecentSubscriber(initialValue); + MostRecentSubscriber mostRecentSubscriber = new MostRecentSubscriber<>(initialValue); source.subscribe(mostRecentSubscriber); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java index 4db920bb1f..9704d2d210 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ /** * Returns an Iterable that blocks until the Observable emits another item, then returns that item. *

- * + * * * @param the value type */ @@ -41,8 +41,8 @@ public BlockingFlowableNext(Publisher source) { @Override public Iterator iterator() { - NextSubscriber nextSubscriber = new NextSubscriber(); - return new NextIterator(source, nextSubscriber); + NextSubscriber nextSubscriber = new NextSubscriber<>(); + return new NextIterator<>(source, nextSubscriber); } // test needs to access the observer.waiting flag @@ -99,11 +99,8 @@ private boolean moveToNext() { if (nextNotification.isOnComplete()) { return false; } - if (nextNotification.isOnError()) { - error = nextNotification.getError(); - throw ExceptionHelper.wrapOrThrow(error); - } - throw new IllegalStateException("Should not reach here"); + error = nextNotification.getError(); + throw ExceptionHelper.wrapOrThrow(error); } catch (InterruptedException e) { subscriber.dispose(); error = e; @@ -133,7 +130,7 @@ public void remove() { } static final class NextSubscriber extends DisposableSubscriber> { - private final BlockingQueue> buf = new ArrayBlockingQueue>(1); + private final BlockingQueue> buf = new ArrayBlockingQueue<>(1); final AtomicInteger waiting = new AtomicInteger(); @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAll.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAll.java index 0933703803..7f8271733d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAll.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAll.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.*; @@ -31,7 +32,7 @@ public FlowableAll(Flowable source, Predicate predicate) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new AllSubscriber(s, predicate)); + source.subscribe(new AllSubscriber<>(s, predicate)); } static final class AllSubscriber extends DeferredScalarSubscription implements FlowableSubscriber { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllSingle.java index 516d37bf7c..03d269e738 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.Subscription; @@ -35,12 +36,12 @@ public FlowableAllSingle(Flowable source, Predicate predicate) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new AllSubscriber(observer, predicate)); + source.subscribe(new AllSubscriber<>(observer, predicate)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableAll(source, predicate)); + return RxJavaPlugins.onAssembly(new FlowableAll<>(source, predicate)); } static final class AllSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmb.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmb.java index 546beb0fae..5646fdb993 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmb.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmb.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -69,7 +69,7 @@ public void subscribeActual(Subscriber s) { return; } - AmbCoordinator ac = new AmbCoordinator(s, count); + AmbCoordinator ac = new AmbCoordinator<>(s, count); ac.subscribe(sources); } @@ -89,7 +89,7 @@ public void subscribe(Publisher[] sources) { AmbInnerSubscriber[] as = subscribers; int len = as.length; for (int i = 0; i < len; i++) { - as[i] = new AmbInnerSubscriber(this, i + 1, downstream); + as[i] = new AmbInnerSubscriber<>(this, i + 1, downstream); } winner.lazySet(0); // release the contents of 'as' downstream.onSubscribe(this); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAny.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAny.java index 401d444cca..b09dce4c03 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAny.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAny.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.*; @@ -29,7 +30,7 @@ public FlowableAny(Flowable source, Predicate predicate) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new AnySubscriber(s, predicate)); + source.subscribe(new AnySubscriber<>(s, predicate)); } static final class AnySubscriber extends DeferredScalarSubscription implements FlowableSubscriber { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnySingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnySingle.java index 6a9e4aacbb..1b210b82d4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnySingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnySingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.Subscription; @@ -34,12 +35,12 @@ public FlowableAnySingle(Flowable source, Predicate predicate) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new AnySubscriber(observer, predicate)); + source.subscribe(new AnySubscriber<>(observer, predicate)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableAny(source, predicate)); + return RxJavaPlugins.onAssembly(new FlowableAny<>(source, predicate)); } static final class AnySubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnect.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnect.java index c8bb4a7d1a..8d4b428c2b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnect.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnect.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingSubscribe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingSubscribe.java index 899ae6c21c..22cede1410 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.*; import org.reactivestreams.*; @@ -35,17 +36,17 @@ private FlowableBlockingSubscribe() { /** * Subscribes to the source and calls the Subscriber methods on the current thread. *

- * @param o the source publisher + * @param source the source publisher * The cancellation and backpressure is composed through. * @param subscriber the subscriber to forward events and calls to in the current thread * @param the value type */ - public static void subscribe(Publisher o, Subscriber subscriber) { - final BlockingQueue queue = new LinkedBlockingQueue(); + public static void subscribe(Publisher source, Subscriber subscriber) { + final BlockingQueue queue = new LinkedBlockingQueue<>(); - BlockingSubscriber bs = new BlockingSubscriber(queue); + BlockingSubscriber bs = new BlockingSubscriber<>(queue); - o.subscribe(bs); + source.subscribe(bs); try { for (;;) { @@ -76,15 +77,15 @@ public static void subscribe(Publisher o, Subscriber /** * Runs the source observable to a terminal event, ignoring any values and rethrowing any exception. - * @param o the source publisher + * @param source the source to await * @param the value type */ - public static void subscribe(Publisher o) { + public static void subscribe(Publisher source) { BlockingIgnoringReceiver callback = new BlockingIgnoringReceiver(); - LambdaSubscriber ls = new LambdaSubscriber(Functions.emptyConsumer(), + LambdaSubscriber ls = new LambdaSubscriber<>(Functions.emptyConsumer(), callback, callback, Functions.REQUEST_MAX); - o.subscribe(ls); + source.subscribe(ls); BlockingHelper.awaitForComplete(callback, ls); Throwable e = callback.error; @@ -103,9 +104,9 @@ public static void subscribe(Publisher o) { */ public static void subscribe(Publisher o, final Consumer onNext, final Consumer onError, final Action onComplete) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); subscribe(o, new LambdaSubscriber(onNext, onError, onComplete, Functions.REQUEST_MAX)); } @@ -120,9 +121,9 @@ public static void subscribe(Publisher o, final Consumer void subscribe(Publisher o, final Consumer onNext, final Consumer onError, final Action onComplete, int bufferSize) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); ObjectHelper.verifyPositive(bufferSize, "number > 0 required"); subscribe(o, new BoundedSubscriber(onNext, onError, onComplete, Functions.boundedConsumer(bufferSize), bufferSize)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java index 0da3aea64e..a1cb792f08 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBuffer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -43,11 +42,11 @@ public FlowableBuffer(Flowable source, int size, int skip, Supplier buffer @Override public void subscribeActual(Subscriber s) { if (size == skip) { - source.subscribe(new PublisherBufferExactSubscriber(s, size, bufferSupplier)); + source.subscribe(new PublisherBufferExactSubscriber<>(s, size, bufferSupplier)); } else if (skip > size) { - source.subscribe(new PublisherBufferSkipSubscriber(s, size, skip, bufferSupplier)); + source.subscribe(new PublisherBufferSkipSubscriber<>(s, size, skip, bufferSupplier)); } else { - source.subscribe(new PublisherBufferOverlappingSubscriber(s, size, skip, bufferSupplier)); + source.subscribe(new PublisherBufferOverlappingSubscriber<>(s, size, skip, bufferSupplier)); } } @@ -105,7 +104,7 @@ public void onNext(T t) { if (b == null) { try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); + b = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -134,6 +133,7 @@ public void onError(Throwable t) { RxJavaPlugins.onError(t); return; } + buffer = null; done = true; downstream.onError(t); } @@ -146,8 +146,9 @@ public void onComplete() { done = true; C b = buffer; + buffer = null; - if (b != null && !b.isEmpty()) { + if (b != null) { downstream.onNext(b); } downstream.onComplete(); @@ -227,7 +228,7 @@ public void onNext(T t) { if (i++ == 0) { try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); + b = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -319,7 +320,7 @@ static final class PublisherBufferOverlappingSubscriber(); + this.buffers = new ArrayDeque<>(); } @Override @@ -378,7 +379,7 @@ public void onNext(T t) { C b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); + b = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -391,7 +392,7 @@ public void onNext(T t) { C b = bs.peek(); - if (b != null && b.size() + 1 == size) { + if (b.size() + 1 == size) { bs.poll(); b.add(t); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferBoundary.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferBoundary.java index 4e8103e1a6..b009b1bc25 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferBoundary.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferBoundary.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,10 +22,9 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableBufferBoundary, Open, Close> @@ -45,7 +44,7 @@ public FlowableBufferBoundary(Flowable source, Publisher buff @Override protected void subscribeActual(Subscriber s) { BufferBoundarySubscriber parent = - new BufferBoundarySubscriber( + new BufferBoundarySubscriber<>( s, bufferOpen, bufferClose, bufferSupplier ); s.onSubscribe(parent); @@ -94,11 +93,11 @@ static final class BufferBoundarySubscriber, this.bufferSupplier = bufferSupplier; this.bufferOpen = bufferOpen; this.bufferClose = bufferClose; - this.queue = new SpscLinkedArrayQueue(bufferSize()); + this.queue = new SpscLinkedArrayQueue<>(bufferSize()); this.subscribers = new CompositeDisposable(); this.requested = new AtomicLong(); - this.upstream = new AtomicReference(); - this.buffers = new LinkedHashMap(); + this.upstream = new AtomicReference<>(); + this.buffers = new LinkedHashMap<>(); this.errors = new AtomicThrowable(); } @@ -106,7 +105,7 @@ static final class BufferBoundarySubscriber, public void onSubscribe(Subscription s) { if (SubscriptionHelper.setOnce(this.upstream, s)) { - BufferOpenSubscriber open = new BufferOpenSubscriber(this); + BufferOpenSubscriber open = new BufferOpenSubscriber<>(this); subscribers.add(open); bufferOpen.subscribe(open); @@ -181,8 +180,8 @@ void open(Open token) { Publisher p; C buf; try { - buf = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null Collection"); - p = ObjectHelper.requireNonNull(bufferClose.apply(token), "The bufferClose returned a null Publisher"); + buf = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null Collection"); + p = Objects.requireNonNull(bufferClose.apply(token), "The bufferClose returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); SubscriptionHelper.cancel(upstream); @@ -200,7 +199,7 @@ void open(Open token) { bufs.put(idx, buf); } - BufferCloseSubscriber bc = new BufferCloseSubscriber(this, idx); + BufferCloseSubscriber bc = new BufferCloseSubscriber<>(this, idx); subscribers.add(bc); p.subscribe(bc); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferExactBoundary.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferExactBoundary.java index 22d139eb89..3e2abefdd9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferExactBoundary.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferExactBoundary.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; import java.util.Collection; +import java.util.Objects; import org.reactivestreams.*; @@ -21,7 +22,6 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; import io.reactivex.rxjava3.internal.subscribers.QueueDrainSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; @@ -41,11 +41,11 @@ public FlowableBufferExactBoundary(Flowable source, Publisher boundary, Su @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new BufferExactBoundarySubscriber(new SerializedSubscriber(s), bufferSupplier, boundary)); + source.subscribe(new BufferExactBoundarySubscriber<>(new SerializedSubscriber<>(s), bufferSupplier, boundary)); } static final class BufferExactBoundarySubscriber, B> - extends QueueDrainSubscriber implements FlowableSubscriber, Subscription, Disposable { + extends QueueDrainSubscriber implements Subscription, Disposable { final Supplier bufferSupplier; final Publisher boundary; @@ -58,7 +58,7 @@ static final class BufferExactBoundarySubscriber actual, Supplier bufferSupplier, Publisher boundary) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.boundary = boundary; } @@ -73,7 +73,7 @@ public void onSubscribe(Subscription s) { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancelled = true; @@ -84,7 +84,7 @@ public void onSubscribe(Subscription s) { buffer = b; - BufferBoundarySubscriber bs = new BufferBoundarySubscriber(this); + BufferBoundarySubscriber bs = new BufferBoundarySubscriber<>(this); other = bs; downstream.onSubscribe(this); @@ -153,7 +153,7 @@ void next() { U next; try { - next = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + next = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTimed.java index 4057a06abb..89749acaef 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; import io.reactivex.rxjava3.internal.subscribers.QueueDrainSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; @@ -57,16 +56,16 @@ public FlowableBufferTimed(Flowable source, long timespan, long timeskip, Tim @Override protected void subscribeActual(Subscriber s) { if (timespan == timeskip && maxSize == Integer.MAX_VALUE) { - source.subscribe(new BufferExactUnboundedSubscriber( - new SerializedSubscriber(s), + source.subscribe(new BufferExactUnboundedSubscriber<>( + new SerializedSubscriber<>(s), bufferSupplier, timespan, unit, scheduler)); return; } Scheduler.Worker w = scheduler.createWorker(); if (timespan == timeskip) { - source.subscribe(new BufferExactBoundedSubscriber( - new SerializedSubscriber(s), + source.subscribe(new BufferExactBoundedSubscriber<>( + new SerializedSubscriber<>(s), bufferSupplier, timespan, unit, maxSize, restartTimerOnMaxSize, w )); @@ -74,8 +73,8 @@ protected void subscribeActual(Subscriber s) { } // Can't use maxSize because what to do if a buffer is full but its // timespan hasn't been elapsed? - source.subscribe(new BufferSkipBoundedSubscriber( - new SerializedSubscriber(s), + source.subscribe(new BufferSkipBoundedSubscriber<>( + new SerializedSubscriber<>(s), bufferSupplier, timespan, timeskip, unit, w)); } @@ -90,12 +89,12 @@ static final class BufferExactUnboundedSubscriber timer = new AtomicReference(); + final AtomicReference timer = new AtomicReference<>(); BufferExactUnboundedSubscriber( Subscriber actual, Supplier bufferSupplier, long timespan, TimeUnit unit, Scheduler scheduler) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.timespan = timespan; this.unit = unit; @@ -110,7 +109,7 @@ public void onSubscribe(Subscription s) { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -187,7 +186,7 @@ public void run() { U next; try { - next = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + next = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -239,13 +238,13 @@ static final class BufferSkipBoundedSubscriber actual, Supplier bufferSupplier, long timespan, long timeskip, TimeUnit unit, Worker w) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.timespan = timespan; this.timeskip = timeskip; this.unit = unit; this.w = w; - this.buffers = new LinkedList(); + this.buffers = new LinkedList<>(); } @Override @@ -258,7 +257,7 @@ public void onSubscribe(Subscription s) { final U b; // NOPMD try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); w.dispose(); @@ -299,7 +298,7 @@ public void onError(Throwable t) { public void onComplete() { List bs; synchronized (this) { - bs = new ArrayList(buffers); + bs = new ArrayList<>(buffers); buffers.clear(); } @@ -339,7 +338,7 @@ public void run() { final U b; // NOPMD try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -405,7 +404,7 @@ static final class BufferExactBoundedSubscriber bufferSupplier, long timespan, TimeUnit unit, int maxSize, boolean restartOnMaxSize, Worker w) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.timespan = timespan; this.unit = unit; @@ -424,7 +423,7 @@ public void onSubscribe(Subscription s) { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); w.dispose(); @@ -468,7 +467,7 @@ public void onNext(T t) { fastPathOrderedEmitMax(b, false, this); try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -550,7 +549,7 @@ public void run() { U next; try { - next = ObjectHelper.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); + next = Objects.requireNonNull(bufferSupplier.get(), "The supplied buffer is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCache.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCache.java index aea0b3150d..ee52769e69 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCache.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCache.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -99,15 +99,15 @@ public FlowableCache(Flowable source, int capacityHint) { super(source); this.capacityHint = capacityHint; this.once = new AtomicBoolean(); - Node n = new Node(capacityHint); + Node n = new Node<>(capacityHint); this.head = n; this.tail = n; - this.subscribers = new AtomicReference[]>(EMPTY); + this.subscribers = new AtomicReference<>(EMPTY); } @Override protected void subscribeActual(Subscriber t) { - CacheSubscription consumer = new CacheSubscription(t, this); + CacheSubscription consumer = new CacheSubscription<>(t, this); t.onSubscribe(consumer); add(consumer); @@ -303,7 +303,7 @@ public void onNext(T t) { int tailOffset = this.tailOffset; // if the current tail node is full, create a fresh node if (tailOffset == capacityHint) { - Node n = new Node(tailOffset); + Node n = new Node<>(tailOffset); n.values[0] = t; this.tailOffset = 1; tail.next = n; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollect.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollect.java index d5b4dd8233..8f45d62404 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollect.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollect.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.*; @@ -17,10 +18,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class FlowableCollect extends AbstractFlowableWithUpstream { final Supplier initialSupplier; @@ -36,13 +38,14 @@ public FlowableCollect(Flowable source, Supplier initialSupplier protected void subscribeActual(Subscriber s) { U u; try { - u = ObjectHelper.requireNonNull(initialSupplier.get(), "The initial value supplied is null"); + u = Objects.requireNonNull(initialSupplier.get(), "The initial value supplied is null"); } catch (Throwable e) { + Exceptions.throwIfFatal(e); EmptySubscription.error(e, s); return; } - source.subscribe(new CollectSubscriber(s, u, collector)); + source.subscribe(new CollectSubscriber<>(s, u, collector)); } static final class CollectSubscriber extends DeferredScalarSubscription implements FlowableSubscriber { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollectSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollectSingle.java index b48f4d11fc..714bc92eea 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollectSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCollectSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.Subscription; @@ -19,11 +20,12 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.FuseToFlowable; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class FlowableCollectSingle extends Single implements FuseToFlowable { final Flowable source; @@ -41,18 +43,19 @@ public FlowableCollectSingle(Flowable source, Supplier initialSu protected void subscribeActual(SingleObserver observer) { U u; try { - u = ObjectHelper.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); + u = Objects.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); } catch (Throwable e) { + Exceptions.throwIfFatal(e); EmptyDisposable.error(e, observer); return; } - source.subscribe(new CollectSubscriber(observer, u, collector)); + source.subscribe(new CollectSubscriber<>(observer, u, collector)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableCollect(source, initialSupplier, collector)); + return RxJavaPlugins.onAssembly(new FlowableCollect<>(source, initialSupplier, collector)); } static final class CollectSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java index 70f7f313bc..ac947b9541 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,7 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; -import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,11 +22,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.flowable.FlowableMap.MapSubscriber; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -73,75 +72,46 @@ public FlowableCombineLatest(@NonNull Iterable> @SuppressWarnings("unchecked") @Override public void subscribeActual(Subscriber s) { - Publisher[] a = array; - int n; - if (a == null) { - n = 0; - a = new Publisher[8]; - - Iterator> it; + Publisher[] sources = array; + int count; + if (sources == null) { + count = 0; + sources = new Publisher[8]; try { - it = ObjectHelper.requireNonNull(iterable.iterator(), "The iterator returned is null"); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - EmptySubscription.error(e, s); - return; - } - - for (;;) { - - boolean b; - - try { - b = it.hasNext(); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - EmptySubscription.error(e, s); - return; - } - - if (!b) { - break; - } - - Publisher p; - - try { - p = ObjectHelper.requireNonNull(it.next(), "The publisher returned by the iterator is null"); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - EmptySubscription.error(e, s); - return; - } - - if (n == a.length) { - Publisher[] c = new Publisher[n + (n >> 2)]; - System.arraycopy(a, 0, c, 0, n); - a = c; + for (Publisher p : iterable) { + if (count == sources.length) { + Publisher[] b = new Publisher[count + (count >> 2)]; + System.arraycopy(sources, 0, b, 0, count); + sources = b; + } + sources[count++] = Objects.requireNonNull(p, "The Iterator returned a null Publisher"); } - a[n++] = p; + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptySubscription.error(ex, s); + return; } } else { - n = a.length; + count = sources.length; } - if (n == 0) { + if (count == 0) { EmptySubscription.complete(s); return; } - if (n == 1) { - ((Publisher)a[0]).subscribe(new MapSubscriber(s, new SingletonArrayFunc())); + if (count == 1) { + sources[0].subscribe(new MapSubscriber<>(s, new SingletonArrayFunc())); return; } CombineLatestCoordinator coordinator = - new CombineLatestCoordinator(s, combiner, n, bufferSize, delayErrors); + new CombineLatestCoordinator<>(s, combiner, count, bufferSize, delayErrors); s.onSubscribe(coordinator); - coordinator.subscribe(a, n); + coordinator.subscribe(sources, count); } static final class CombineLatestCoordinator @@ -173,7 +143,7 @@ static final class CombineLatestCoordinator volatile boolean done; - final AtomicReference error; + final AtomicThrowable error; CombineLatestCoordinator(Subscriber actual, Function combiner, int n, @@ -183,13 +153,13 @@ static final class CombineLatestCoordinator @SuppressWarnings("unchecked") CombineLatestInnerSubscriber[] a = new CombineLatestInnerSubscriber[n]; for (int i = 0; i < n; i++) { - a[i] = new CombineLatestInnerSubscriber(this, i, bufferSize); + a[i] = new CombineLatestInnerSubscriber<>(this, i, bufferSize); } this.subscribers = a; this.latest = new Object[n]; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.requested = new AtomicLong(); - this.error = new AtomicReference(); + this.error = new AtomicThrowable(); this.delayErrors = delayErrors; } @@ -205,6 +175,7 @@ public void request(long n) { public void cancel() { cancelled = true; cancelAll(); + drain(); } void subscribe(Publisher[] sources, int n) { @@ -359,7 +330,7 @@ void drainAsync() { R w; try { - w = ObjectHelper.requireNonNull(combiner.apply(va), "The combiner returned a null value"); + w = Objects.requireNonNull(combiner.apply(va), "The combiner returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); @@ -411,6 +382,7 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, SpscLinkedArr if (cancelled) { cancelAll(); q.clear(); + error.tryTerminateAndReport(); return true; } @@ -418,13 +390,7 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, SpscLinkedArr if (delayErrors) { if (empty) { cancelAll(); - Throwable e = ExceptionHelper.terminate(error); - - if (e != null && e != ExceptionHelper.TERMINATED) { - a.onError(e); - } else { - a.onComplete(); - } + error.tryTerminateConsumer(a); return true; } } else { @@ -472,7 +438,7 @@ public R poll() throws Throwable { return null; } T[] a = (T[])queue.poll(); - R r = ObjectHelper.requireNonNull(combiner.apply(a), "The combiner returned a null value"); + R r = Objects.requireNonNull(combiner.apply(a), "The combiner returned a null value"); ((CombineLatestInnerSubscriber)e).requestOne(); return r; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatArray.java index ad1651b261..34d4248514 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import java.util.*; @@ -34,7 +35,7 @@ public FlowableConcatArray(Publisher[] sources, boolean delayError) @Override protected void subscribeActual(Subscriber s) { - ConcatArraySubscriber parent = new ConcatArraySubscriber(sources, delayError, s); + ConcatArraySubscriber parent = new ConcatArraySubscriber<>(sources, delayError, s); s.onSubscribe(parent); parent.onComplete(); @@ -82,7 +83,7 @@ public void onError(Throwable t) { if (delayError) { List list = errors; if (list == null) { - list = new ArrayList(sources.length - index + 1); + list = new ArrayList<>(sources.length - index + 1); errors = list; } list.add(t); @@ -121,7 +122,7 @@ public void onComplete() { if (delayError) { List list = errors; if (list == null) { - list = new ArrayList(n - i + 1); + list = new ArrayList<>(n - i + 1); errors = list; } list.add(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java index a11f206dde..5d1b6e4c33 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,20 +10,22 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Objects; +import java.util.concurrent.atomic.*; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; public final class FlowableConcatMap extends AbstractFlowableWithUpstream { @@ -46,11 +48,11 @@ public static Subscriber subscribe(Subscriber s, Function(s, mapper, prefetch, false); + return new ConcatMapDelayed<>(s, mapper, prefetch, false); case END: - return new ConcatMapDelayed(s, mapper, prefetch, true); + return new ConcatMapDelayed<>(s, mapper, prefetch, true); default: - return new ConcatMapImmediate(s, mapper, prefetch); + return new ConcatMapImmediate<>(s, mapper, prefetch); } } @@ -100,7 +102,7 @@ abstract static class BaseConcatMapSubscriber this.mapper = mapper; this.prefetch = prefetch; this.limit = prefetch - (prefetch >> 2); - this.inner = new ConcatMapInner(this); + this.inner = new ConcatMapInner<>(this); this.errors = new AtomicThrowable(); } @@ -133,7 +135,7 @@ public final void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); subscribeActual(); @@ -150,7 +152,7 @@ public final void onNext(T t) { if (sourceMode != QueueSubscription.ASYNC) { if (!queue.offer(t)) { upstream.cancel(); - onError(new IllegalStateException("Queue full?!")); + onError(new QueueOverflowException()); return; } } @@ -195,35 +197,19 @@ void subscribeActual() { @Override public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - inner.cancel(); - - if (getAndIncrement() == 0) { - errors.tryTerminateConsumer(downstream); - } - } + inner.cancel(); + HalfSerializer.onError(downstream, t, this, errors); } @Override public void innerNext(R value) { - if (get() == 0 && compareAndSet(0, 1)) { - downstream.onNext(value); - if (compareAndSet(1, 0)) { - return; - } - errors.tryTerminateConsumer(downstream); - } + HalfSerializer.onNext(downstream, value, this, errors); } @Override public void innerError(Throwable e) { - if (errors.tryAddThrowableOrReport(e)) { - upstream.cancel(); - - if (getAndIncrement() == 0) { - errors.tryTerminateConsumer(downstream); - } - } + upstream.cancel(); + HalfSerializer.onError(downstream, e, this, errors); } @Override @@ -277,7 +263,7 @@ void drain() { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); @@ -318,17 +304,13 @@ void drain() { } if (inner.isUnbounded()) { - if (get() == 0 && compareAndSet(0, 1)) { - downstream.onNext(vr); - if (!compareAndSet(1, 0)) { - errors.tryTerminateConsumer(downstream); - return; - } + if (!HalfSerializer.onNext(downstream, vr, this, errors)) { + return; } continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { @@ -345,20 +327,22 @@ void drain() { } } - static final class WeakScalarSubscription implements Subscription { + static final class SimpleScalarSubscription + extends AtomicBoolean + implements Subscription { + private static final long serialVersionUID = -7606889335172043256L; + final Subscriber downstream; final T value; - boolean once; - WeakScalarSubscription(T value, Subscriber downstream) { + SimpleScalarSubscription(T value, Subscriber downstream) { this.value = value; this.downstream = downstream; } @Override public void request(long n) { - if (n > 0 && !once) { - once = true; + if (n > 0L && compareAndSet(false, true)) { Subscriber a = downstream; a.onNext(value); a.onComplete(); @@ -479,7 +463,7 @@ void drain() { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); @@ -527,7 +511,7 @@ void drain() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { active = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java index 29c2077226..dc423d89fc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEager.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -20,12 +21,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscribers.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class FlowableConcatMapEager extends AbstractFlowableWithUpstream { @@ -51,7 +51,7 @@ public FlowableConcatMapEager(Flowable source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatMapEagerDelayErrorSubscriber( + source.subscribe(new ConcatMapEagerDelayErrorSubscriber<>( s, mapper, maxConcurrency, prefetch, errorMode)); } @@ -93,7 +93,7 @@ static final class ConcatMapEagerDelayErrorSubscriber this.maxConcurrency = maxConcurrency; this.prefetch = prefetch; this.errorMode = errorMode; - this.subscribers = new SpscLinkedArrayQueue>(Math.min(prefetch, maxConcurrency)); + this.subscribers = new SpscLinkedArrayQueue<>(Math.min(prefetch, maxConcurrency)); this.errors = new AtomicThrowable(); this.requested = new AtomicLong(); } @@ -115,7 +115,7 @@ public void onNext(T t) { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -123,7 +123,7 @@ public void onNext(T t) { return; } - InnerQueuedSubscriber inner = new InnerQueuedSubscriber(this, prefetch); + InnerQueuedSubscriber inner = new InnerQueuedSubscriber<>(this, prefetch); if (cancelled) { return; @@ -200,7 +200,7 @@ public void innerNext(InnerQueuedSubscriber inner, R value) { drain(); } else { inner.cancel(); - innerError(inner, new MissingBackpressureException()); + innerError(inner, MissingBackpressureException.createDefault()); } } @@ -318,7 +318,7 @@ public void drain() { e++; - inner.requestOne(); + inner.request(1L); } if (e == r) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerPublisher.java index 8b1e7ec68b..218f90c2a6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,7 +53,7 @@ public FlowableConcatMapEagerPublisher(Publisher source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatMapEagerDelayErrorSubscriber( + source.subscribe(new ConcatMapEagerDelayErrorSubscriber<>( s, mapper, maxConcurrency, prefetch, errorMode)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java index 828bb5a5fa..707d10a19e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,21 +10,23 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; public final class FlowableConcatMapScheduler extends AbstractFlowableWithUpstream { @@ -50,13 +52,13 @@ public FlowableConcatMapScheduler(Flowable source, protected void subscribeActual(Subscriber s) { switch (errorMode) { case BOUNDARY: - source.subscribe(new ConcatMapDelayed(s, mapper, prefetch, false, scheduler.createWorker())); + source.subscribe(new ConcatMapDelayed<>(s, mapper, prefetch, false, scheduler.createWorker())); break; case END: - source.subscribe(new ConcatMapDelayed(s, mapper, prefetch, true, scheduler.createWorker())); + source.subscribe(new ConcatMapDelayed<>(s, mapper, prefetch, true, scheduler.createWorker())); break; default: - source.subscribe(new ConcatMapImmediate(s, mapper, prefetch, scheduler.createWorker())); + source.subscribe(new ConcatMapImmediate<>(s, mapper, prefetch, scheduler.createWorker())); } } @@ -98,7 +100,7 @@ abstract static class BaseConcatMapSubscriber this.mapper = mapper; this.prefetch = prefetch; this.limit = prefetch - (prefetch >> 2); - this.inner = new ConcatMapInner(this); + this.inner = new ConcatMapInner<>(this); this.errors = new AtomicThrowable(); this.worker = worker; } @@ -132,7 +134,7 @@ public final void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); subscribeActual(); @@ -149,7 +151,7 @@ public final void onNext(T t) { if (sourceMode != QueueSubscription.ASYNC) { if (!queue.offer(t)) { upstream.cancel(); - onError(new IllegalStateException("Queue full?!")); + onError(new QueueOverflowException()); return; } } @@ -204,9 +206,13 @@ public void onError(Throwable t) { } } + boolean tryEnter() { + return get() == 0 && compareAndSet(0, 1); + } + @Override public void innerNext(R value) { - if (get() == 0 && compareAndSet(0, 1)) { + if (tryEnter()) { downstream.onNext(value); if (compareAndSet(1, 0)) { return; @@ -287,7 +293,7 @@ public void run() { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); @@ -325,12 +331,12 @@ public void run() { return; } - if (vr == null) { + if (vr == null || cancelled) { continue; } if (inner.isUnbounded()) { - if (get() == 0 && compareAndSet(0, 1)) { + if (tryEnter()) { downstream.onNext(vr); if (!compareAndSet(1, 0)) { errors.tryTerminateConsumer(downstream); @@ -341,7 +347,7 @@ public void run() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { @@ -474,7 +480,7 @@ public void run() { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); @@ -515,7 +521,7 @@ public void run() { vr = null; } - if (vr == null) { + if (vr == null || cancelled) { continue; } @@ -524,7 +530,7 @@ public void run() { continue; } else { active = true; - inner.setSubscription(new WeakScalarSubscription(vr, inner)); + inner.setSubscription(new SimpleScalarSubscription<>(vr, inner)); } } else { active = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletable.java index 16556a5892..81ac13776a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public FlowableConcatWithCompletable(Flowable source, CompletableSource other @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatWithSubscriber(s, other)); + source.subscribe(new ConcatWithSubscriber<>(s, other)); } static final class ConcatWithSubscriber diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybe.java index 92c65809c7..e82076533a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public FlowableConcatWithMaybe(Flowable source, MaybeSource othe @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatWithSubscriber(s, other)); + source.subscribe(new ConcatWithSubscriber<>(s, other)); } static final class ConcatWithSubscriber @@ -59,7 +59,7 @@ static final class ConcatWithSubscriber ConcatWithSubscriber(Subscriber actual, MaybeSource other) { super(actual); this.other = other; - this.otherDisposable = new AtomicReference(); + this.otherDisposable = new AtomicReference<>(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingle.java index b1b65d9dbf..bfe900ab2e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public FlowableConcatWithSingle(Flowable source, SingleSource ot @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatWithSubscriber(s, other)); + source.subscribe(new ConcatWithSubscriber<>(s, other)); } static final class ConcatWithSubscriber @@ -57,7 +57,7 @@ static final class ConcatWithSubscriber ConcatWithSubscriber(Subscriber actual, SingleSource other) { super(actual); this.other = other; - this.otherDisposable = new AtomicReference(); + this.otherDisposable = new AtomicReference<>(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCount.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCount.java index 7858180b09..2c32dd4d76 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCount.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCount.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountSingle.java index 517d026e3f..750f722dbc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ protected void subscribeActual(SingleObserver observer) { @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableCount(source)); + return RxJavaPlugins.onAssembly(new FlowableCount<>(source)); } static final class CountSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreate.java index 66cbd7409b..1e87c4b7e6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,10 +22,10 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Cancellable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableCreate extends Flowable { @@ -45,23 +45,23 @@ public void subscribeActual(Subscriber t) { switch (backpressure) { case MISSING: { - emitter = new MissingEmitter(t); + emitter = new MissingEmitter<>(t); break; } case ERROR: { - emitter = new ErrorAsyncEmitter(t); + emitter = new ErrorAsyncEmitter<>(t); break; } case DROP: { - emitter = new DropAsyncEmitter(t); + emitter = new DropAsyncEmitter<>(t); break; } case LATEST: { - emitter = new LatestAsyncEmitter(t); + emitter = new LatestAsyncEmitter<>(t); break; } default: { - emitter = new BufferAsyncEmitter(t, bufferSize()); + emitter = new BufferAsyncEmitter<>(t, bufferSize()); break; } } @@ -97,7 +97,7 @@ static final class SerializedEmitter SerializedEmitter(BaseEmitter emitter) { this.emitter = emitter; this.errors = new AtomicThrowable(); - this.queue = new SpscLinkedArrayQueue(16); + this.queue = new SpscLinkedArrayQueue<>(16); } @Override @@ -347,7 +347,7 @@ public final long requested() { @Override public final FlowableEmitter serialize() { - return new SerializedEmitter(this); + return new SerializedEmitter<>(this); } @Override @@ -442,7 +442,7 @@ static final class ErrorAsyncEmitter extends NoOverflowBaseAsyncEmitter { @Override void onOverflow() { - onError(new MissingBackpressureException("create: could not emit value due to lack of requests")); + onError(new MissingBackpressureException("create: " + MissingBackpressureException.DEFAULT_MESSAGE)); } } @@ -460,7 +460,7 @@ static final class BufferAsyncEmitter extends BaseEmitter { BufferAsyncEmitter(Subscriber actual, int capacityHint) { super(actual); - this.queue = new SpscLinkedArrayQueue(capacityHint); + this.queue = new SpscLinkedArrayQueue<>(capacityHint); this.wip = new AtomicInteger(); } @@ -598,7 +598,7 @@ static final class LatestAsyncEmitter extends BaseEmitter { LatestAsyncEmitter(Subscriber downstream) { super(downstream); - this.queue = new AtomicReference(); + this.queue = new AtomicReference<>(); this.wip = new AtomicInteger(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounce.java index 53f3c4e833..51838a1864 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounce.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounce.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,7 +23,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -38,7 +38,7 @@ public FlowableDebounce(Flowable source, Function s) { - source.subscribe(new DebounceSubscriber(new SerializedSubscriber(s), debounceSelector)); + source.subscribe(new DebounceSubscriber<>(new SerializedSubscriber<>(s), debounceSelector)); } static final class DebounceSubscriber extends AtomicLong @@ -50,7 +50,7 @@ static final class DebounceSubscriber extends AtomicLong Subscription upstream; - final AtomicReference debouncer = new AtomicReference(); + final AtomicReference debouncer = new AtomicReference<>(); volatile long index; @@ -88,7 +88,7 @@ public void onNext(T t) { Publisher p; try { - p = ObjectHelper.requireNonNull(debounceSelector.apply(t), "The publisher supplied is null"); + p = Objects.requireNonNull(debounceSelector.apply(t), "The publisher supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); @@ -96,7 +96,7 @@ public void onNext(T t) { return; } - DebounceInnerSubscriber dis = new DebounceInnerSubscriber(this, idx, t); + DebounceInnerSubscriber dis = new DebounceInnerSubscriber<>(this, idx, t); if (debouncer.compareAndSet(d, dis)) { p.subscribe(dis); @@ -148,7 +148,7 @@ void emit(long idx, T value) { BackpressureHelper.produced(this, 1); } else { cancel(); - downstream.onError(new MissingBackpressureException("Could not deliver value due to lack of requests")); + downstream.onError(MissingBackpressureException.createDefault()); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTimed.java index 582c04c6e5..b9e5dff7f3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.*; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; @@ -32,19 +34,20 @@ public final class FlowableDebounceTimed extends AbstractFlowableWithUpstream final long timeout; final TimeUnit unit; final Scheduler scheduler; + final Consumer onDropped; - public FlowableDebounceTimed(Flowable source, long timeout, TimeUnit unit, Scheduler scheduler) { + public FlowableDebounceTimed(Flowable source, long timeout, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { super(source); this.timeout = timeout; this.unit = unit; this.scheduler = scheduler; + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new DebounceTimedSubscriber( - new SerializedSubscriber(s), - timeout, unit, scheduler.createWorker())); + source.subscribe(new DebounceTimedSubscriber<>( + new SerializedSubscriber<>(s), timeout, unit, scheduler.createWorker(), onDropped)); } static final class DebounceTimedSubscriber extends AtomicLong @@ -55,20 +58,22 @@ static final class DebounceTimedSubscriber extends AtomicLong final long timeout; final TimeUnit unit; final Scheduler.Worker worker; + final Consumer onDropped; Subscription upstream; - Disposable timer; + DebounceEmitter timer; volatile long index; boolean done; - DebounceTimedSubscriber(Subscriber actual, long timeout, TimeUnit unit, Worker worker) { + DebounceTimedSubscriber(Subscriber actual, long timeout, TimeUnit unit, Worker worker, Consumer onDropped) { this.downstream = actual; this.timeout = timeout; this.unit = unit; this.worker = worker; + this.onDropped = onDropped; } @Override @@ -88,15 +93,26 @@ public void onNext(T t) { long idx = index + 1; index = idx; - Disposable d = timer; - if (d != null) { - d.dispose(); + DebounceEmitter currentEmitter = timer; + if (currentEmitter != null) { + currentEmitter.dispose(); + } + + if (onDropped != null && currentEmitter != null) { + try { + onDropped.accept(currentEmitter.value); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + done = true; + downstream.onError(ex); + worker.dispose(); + } } - DebounceEmitter de = new DebounceEmitter(t, idx, this); - timer = de; - d = worker.schedule(de, timeout, unit); - de.setResource(d); + DebounceEmitter newEmitter = new DebounceEmitter<>(t, idx, this); + timer = newEmitter; + newEmitter.setResource(worker.schedule(newEmitter, timeout, unit)); } @Override @@ -121,15 +137,13 @@ public void onComplete() { } done = true; - Disposable d = timer; + DebounceEmitter d = timer; if (d != null) { d.dispose(); } - @SuppressWarnings("unchecked") - DebounceEmitter de = (DebounceEmitter)d; - if (de != null) { - de.emit(); + if (d != null) { + d.emit(); } downstream.onComplete(); @@ -159,7 +173,7 @@ void emit(long idx, T t, DebounceEmitter emitter) { emitter.dispose(); } else { cancel(); - downstream.onError(new MissingBackpressureException("Could not deliver value due to lack of requests")); + downstream.onError(MissingBackpressureException.createDefault()); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefer.java index 6117d56f92..b3ea3f1ecc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; +import java.util.Objects; + public final class FlowableDefer extends Flowable { final Supplier> supplier; public FlowableDefer(Supplier> supplier) { @@ -31,7 +32,7 @@ public FlowableDefer(Supplier> supplier) { public void subscribeActual(Subscriber s) { Publisher pub; try { - pub = ObjectHelper.requireNonNull(supplier.get(), "The publisher supplied is null"); + pub = Objects.requireNonNull(supplier.get(), "The publisher supplied is null"); } catch (Throwable t) { Exceptions.throwIfFatal(t); EmptySubscription.error(t, s); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelay.java index 18950363e2..a7de73213a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,12 +42,12 @@ protected void subscribeActual(Subscriber t) { if (delayError) { downstream = t; } else { - downstream = new SerializedSubscriber(t); + downstream = new SerializedSubscriber<>(t); } Scheduler.Worker w = scheduler.createWorker(); - source.subscribe(new DelaySubscriber(downstream, delay, unit, w, delayError)); + source.subscribe(new DelaySubscriber<>(downstream, delay, unit, w, delayError)); } static final class DelaySubscriber implements FlowableSubscriber, Subscription { @@ -111,7 +111,9 @@ final class OnNext implements Runnable { @Override public void run() { - downstream.onNext(t); + if (!w.isDisposed()) { + downstream.onNext(t); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOther.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOther.java index 1ae7f8210e..2f12cc6e4e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOther.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOther.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import java.util.concurrent.atomic.*; @@ -37,7 +38,7 @@ public FlowableDelaySubscriptionOther(Publisher main, Publisher @Override public void subscribeActual(final Subscriber child) { - MainSubscriber parent = new MainSubscriber(child, main); + MainSubscriber parent = new MainSubscriber<>(child, main); child.onSubscribe(parent); other.subscribe(parent.other); } @@ -58,7 +59,7 @@ static final class MainSubscriber extends AtomicLong implements FlowableSubsc this.downstream = downstream; this.main = main; this.other = new OtherSubscriber(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } void next() { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerialize.java index d19bf4f037..10806f5776 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class FlowableDematerialize extends AbstractFlowableWithUpstream { final Function> selector; @@ -33,7 +34,7 @@ public FlowableDematerialize(Flowable source, Function subscriber) { - source.subscribe(new DematerializeSubscriber(subscriber, selector)); + source.subscribe(new DematerializeSubscriber<>(subscriber, selector)); } static final class DematerializeSubscriber implements FlowableSubscriber, Subscription { @@ -74,7 +75,7 @@ public void onNext(T item) { Notification notification; try { - notification = ObjectHelper.requireNonNull(selector.apply(item), "The selector returned a null Notification"); + notification = Objects.requireNonNull(selector.apply(item), "The selector returned a null Notification"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetach.java index e3f266094e..8d755b8ad3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public FlowableDetach(Flowable source) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new DetachSubscriber(s)); + source.subscribe(new DetachSubscriber<>(s)); } static final class DetachSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinct.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinct.java index 870710bd87..7c3c777c26 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinct.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinct.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; import java.util.Collection; +import java.util.Objects; import org.reactivestreams.Subscriber; @@ -21,11 +22,10 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.subscribers.BasicFuseableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableDistinct extends AbstractFlowableWithUpstream { @@ -52,7 +52,7 @@ protected void subscribeActual(Subscriber subscriber) { return; } - source.subscribe(new DistinctSubscriber(subscriber, keySelector, collection)); + source.subscribe(new DistinctSubscriber<>(subscriber, keySelector, collection)); } static final class DistinctSubscriber extends BasicFuseableSubscriber { @@ -77,7 +77,7 @@ public void onNext(T value) { boolean b; try { - key = ObjectHelper.requireNonNull(keySelector.apply(value), "The keySelector returned a null key"); + key = Objects.requireNonNull(keySelector.apply(value), "The keySelector returned a null key"); b = collection.add(key); } catch (Throwable ex) { fail(ex); @@ -125,7 +125,7 @@ public T poll() throws Throwable { for (;;) { T v = qs.poll(); - if (v == null || collection.add(ObjectHelper.requireNonNull(keySelector.apply(v), "The keySelector returned a null key"))) { + if (v == null || collection.add(Objects.requireNonNull(keySelector.apply(v), "The keySelector returned a null key"))) { return v; } else { if (sourceMode == QueueFuseable.ASYNC) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChanged.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChanged.java index f3ee070429..2c597556ec 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChanged.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChanged.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,8 +18,8 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscribers.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; public final class FlowableDistinctUntilChanged extends AbstractFlowableWithUpstream { @@ -37,9 +37,9 @@ public FlowableDistinctUntilChanged(Flowable source, Function k protected void subscribeActual(Subscriber s) { if (s instanceof ConditionalSubscriber) { ConditionalSubscriber cs = (ConditionalSubscriber) s; - source.subscribe(new DistinctUntilChangedConditionalSubscriber(cs, keySelector, comparer)); + source.subscribe(new DistinctUntilChangedConditionalSubscriber<>(cs, keySelector, comparer)); } else { - source.subscribe(new DistinctUntilChangedSubscriber(s, keySelector, comparer)); + source.subscribe(new DistinctUntilChangedSubscriber<>(s, keySelector, comparer)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNext.java index ed5d969328..763e93a54a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,8 +18,8 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscribers.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; /** * Calls a consumer after pushing the current item to the downstream. @@ -39,9 +39,9 @@ public FlowableDoAfterNext(Flowable source, Consumer onAfterNext) @Override protected void subscribeActual(Subscriber s) { if (s instanceof ConditionalSubscriber) { - source.subscribe(new DoAfterConditionalSubscriber((ConditionalSubscriber)s, onAfterNext)); + source.subscribe(new DoAfterConditionalSubscriber<>((ConditionalSubscriber) s, onAfterNext)); } else { - source.subscribe(new DoAfterSubscriber(s, onAfterNext)); + source.subscribe(new DoAfterSubscriber<>(s, onAfterNext)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinally.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinally.java index 8cc70f171c..71da853227 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinally.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinally.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,8 +19,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Action; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -41,9 +42,9 @@ public FlowableDoFinally(Flowable source, Action onFinally) { @Override protected void subscribeActual(Subscriber s) { if (s instanceof ConditionalSubscriber) { - source.subscribe(new DoFinallyConditionalSubscriber((ConditionalSubscriber)s, onFinally)); + source.subscribe(new DoFinallyConditionalSubscriber<>((ConditionalSubscriber) s, onFinally)); } else { - source.subscribe(new DoFinallySubscriber(s, onFinally)); + source.subscribe(new DoFinallySubscriber<>(s, onFinally)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEach.java index b6900b077e..3284a2e507 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,9 +19,9 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscribers.*; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableDoOnEach extends AbstractFlowableWithUpstream { @@ -44,10 +44,10 @@ public FlowableDoOnEach(Flowable source, Consumer onNext, @Override protected void subscribeActual(Subscriber s) { if (s instanceof ConditionalSubscriber) { - source.subscribe(new DoOnEachConditionalSubscriber( - (ConditionalSubscriber)s, onNext, onError, onComplete, onAfterTerminate)); + source.subscribe(new DoOnEachConditionalSubscriber<>( + (ConditionalSubscriber) s, onNext, onError, onComplete, onAfterTerminate)); } else { - source.subscribe(new DoOnEachSubscriber( + source.subscribe(new DoOnEachSubscriber<>( s, onNext, onError, onComplete, onAfterTerminate)); } } @@ -159,6 +159,7 @@ public T poll() throws Throwable { try { onError.accept(ex); } catch (Throwable exc) { + Exceptions.throwIfFatal(exc); throw new CompositeException(ex, exc); } throw ExceptionHelper.throwIfThrowable(ex); @@ -173,6 +174,7 @@ public T poll() throws Throwable { try { onError.accept(ex); } catch (Throwable exc) { + Exceptions.throwIfFatal(exc); throw new CompositeException(ex, exc); } throw ExceptionHelper.throwIfThrowable(ex); @@ -314,6 +316,7 @@ public T poll() throws Throwable { try { onError.accept(ex); } catch (Throwable exc) { + Exceptions.throwIfFatal(exc); throw new CompositeException(ex, exc); } throw ExceptionHelper.throwIfThrowable(ex); @@ -328,6 +331,7 @@ public T poll() throws Throwable { try { onError.accept(ex); } catch (Throwable exc) { + Exceptions.throwIfFatal(exc); throw new CompositeException(ex, exc); } throw ExceptionHelper.throwIfThrowable(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycle.java index 687474a403..8b56c38c70 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.*; @@ -35,7 +36,7 @@ public FlowableDoOnLifecycle(Flowable source, Consumer @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SubscriptionLambdaSubscriber(s, onSubscribe, onRequest, onCancel)); + source.subscribe(new SubscriptionLambdaSubscriber<>(s, onSubscribe, onRequest, onCancel)); } static final class SubscriptionLambdaSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAt.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAt.java index 31c21e2552..eecd3d1c17 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAt.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAt.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public FlowableElementAt(Flowable source, long index, T defaultValue, boolean @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ElementAtSubscriber(s, index, defaultValue, errorOnFewer)); + source.subscribe(new ElementAtSubscriber<>(s, index, defaultValue, errorOnFewer)); } static final class ElementAtSubscriber extends DeferredScalarSubscription implements FlowableSubscriber { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybe.java index 67f4744b76..f62439c577 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,12 +33,12 @@ public FlowableElementAtMaybe(Flowable source, long index) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new ElementAtSubscriber(observer, index)); + source.subscribe(new ElementAtSubscriber<>(observer, index)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableElementAt(source, index, null, false)); + return RxJavaPlugins.onAssembly(new FlowableElementAt<>(source, index, null, false)); } static final class ElementAtSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybePublisher.java new file mode 100644 index 0000000000..b6b16a3e40 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtMaybePublisher.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import org.reactivestreams.Publisher; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableElementAtMaybe.ElementAtSubscriber; + +/** + * Emits the indexth element from a Publisher as a Maybe. + * + * @param the element type of the source + * @since 3.0.0 + */ +public final class FlowableElementAtMaybePublisher extends Maybe { + + final Publisher source; + + final long index; + + public FlowableElementAtMaybePublisher(Publisher source, long index) { + this.source = source; + this.index = index; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new ElementAtSubscriber<>(observer, index)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtSingle.java index 3260307745..95b32bde6d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,12 +38,12 @@ public FlowableElementAtSingle(Flowable source, long index, T defaultValue) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new ElementAtSubscriber(observer, index, defaultValue)); + source.subscribe(new ElementAtSubscriber<>(observer, index, defaultValue)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableElementAt(source, index, defaultValue, true)); + return RxJavaPlugins.onAssembly(new FlowableElementAt<>(source, index, defaultValue, true)); } static final class ElementAtSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableEmpty.java index 4a9bbe7d32..3606ac57d6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,8 +16,8 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; +import io.reactivex.rxjava3.operators.ScalarSupplier; /** * A source Flowable that signals an onSubscribe() + onComplete() only. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableError.java index f65410ff11..190eb53c6b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilter.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilter.java index 52aac8e1f4..74a4413e36 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilter.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,8 +18,9 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.functions.Predicate; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscribers.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueSubscription; public final class FlowableFilter extends AbstractFlowableWithUpstream { final Predicate predicate; @@ -31,10 +32,10 @@ public FlowableFilter(Flowable source, Predicate predicate) { @Override protected void subscribeActual(Subscriber s) { if (s instanceof ConditionalSubscriber) { - source.subscribe(new FilterConditionalSubscriber( - (ConditionalSubscriber)s, predicate)); + source.subscribe(new FilterConditionalSubscriber<>( + (ConditionalSubscriber) s, predicate)); } else { - source.subscribe(new FilterSubscriber(s, predicate)); + source.subscribe(new FilterSubscriber<>(s, predicate)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java index de53c5d5b8..c250d3b165 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -21,11 +22,9 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableFlatMap extends AbstractFlowableWithUpstream { @@ -55,7 +54,7 @@ protected void subscribeActual(Subscriber s) { public static FlowableSubscriber subscribe(Subscriber s, Function> mapper, boolean delayErrors, int maxConcurrency, int bufferSize) { - return new MergeSubscriber(s, mapper, delayErrors, maxConcurrency, bufferSize); + return new MergeSubscriber<>(s, mapper, delayErrors, maxConcurrency, bufferSize); } static final class MergeSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { @@ -76,7 +75,7 @@ static final class MergeSubscriber extends AtomicInteger implements Flowab volatile boolean cancelled; - final AtomicReference[]> subscribers = new AtomicReference[]>(); + final AtomicReference[]> subscribers = new AtomicReference<>(); static final InnerSubscriber[] EMPTY = new InnerSubscriber[0]; @@ -128,7 +127,7 @@ public void onNext(T t) { } Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.cancel(); @@ -150,14 +149,14 @@ public void onNext(T t) { if (u != null) { tryEmitScalar(u); } else { - if (maxConcurrency != Integer.MAX_VALUE && !cancelled - && ++scalarEmitted == scalarLimit) { + if (maxConcurrency != Integer.MAX_VALUE + && !cancelled && ++scalarEmitted == scalarLimit) { scalarEmitted = 0; upstream.request(scalarLimit); } } } else { - InnerSubscriber inner = new InnerSubscriber(this, uniqueId++); + InnerSubscriber inner = new InnerSubscriber<>(this, bufferSize, uniqueId++); if (addInner(inner)) { p.subscribe(inner); } @@ -216,9 +215,9 @@ SimpleQueue getMainQueue() { SimplePlainQueue q = queue; if (q == null) { if (maxConcurrency == Integer.MAX_VALUE) { - q = new SpscLinkedArrayQueue(bufferSize); + q = new SpscLinkedArrayQueue<>(bufferSize); } else { - q = new SpscArrayQueue(maxConcurrency); + q = new SpscArrayQueue<>(maxConcurrency); } queue = q; } @@ -234,8 +233,8 @@ void tryEmitScalar(U value) { if (r != Long.MAX_VALUE) { requested.decrementAndGet(); } - if (maxConcurrency != Integer.MAX_VALUE && !cancelled - && ++scalarEmitted == scalarLimit) { + if (maxConcurrency != Integer.MAX_VALUE + && !cancelled && ++scalarEmitted == scalarLimit) { scalarEmitted = 0; upstream.request(scalarLimit); } @@ -244,8 +243,7 @@ void tryEmitScalar(U value) { q = getMainQueue(); } if (!q.offer(value)) { - onError(new IllegalStateException("Scalar queue full?!")); - return; + onError(new QueueOverflowException()); } } if (decrementAndGet() == 0) { @@ -254,7 +252,7 @@ void tryEmitScalar(U value) { } else { SimpleQueue q = getMainQueue(); if (!q.offer(value)) { - onError(new IllegalStateException("Scalar queue full?!")); + onError(new QueueOverflowException()); return; } if (getAndIncrement() != 0) { @@ -264,15 +262,6 @@ void tryEmitScalar(U value) { drainLoop(); } - SimpleQueue getInnerQueue(InnerSubscriber inner) { - SimpleQueue q = inner.queue; - if (q == null) { - q = new SpscArrayQueue(bufferSize); - inner.queue = q; - } - return q; - } - void tryEmit(U value, InnerSubscriber inner) { if (get() == 0 && compareAndSet(0, 1)) { long r = requested.get(); @@ -285,11 +274,11 @@ void tryEmit(U value, InnerSubscriber inner) { inner.requestMore(1); } else { if (q == null) { - q = getInnerQueue(inner); + q = new SpscArrayQueue<>(bufferSize); + inner.queue = q; } if (!q.offer(value)) { - onError(new MissingBackpressureException("Inner queue full?!")); - return; + onError(new QueueOverflowException()); } } if (decrementAndGet() == 0) { @@ -298,11 +287,11 @@ void tryEmit(U value, InnerSubscriber inner) { } else { SimpleQueue q = inner.queue; if (q == null) { - q = new SpscArrayQueue(bufferSize); + q = new SpscArrayQueue<>(bufferSize); inner.queue = q; } if (!q.offer(value)) { - onError(new MissingBackpressureException("Inner queue full?!")); + onError(new QueueOverflowException()); return; } if (getAndIncrement() != 0) { @@ -321,6 +310,11 @@ public void onError(Throwable t) { } if (errors.tryAddThrowableOrReport(t)) { done = true; + if (!delayErrors) { + for (InnerSubscriber a : subscribers.getAndSet(CANCELLED)) { + a.dispose(); + } + } drain(); } } @@ -379,35 +373,30 @@ void drainLoop() { long replenishMain = 0; if (svq != null) { - for (;;) { - long scalarEmission = 0; - U o = null; - while (r != 0L) { - o = svq.poll(); - - if (checkTerminate()) { - return; - } - if (o == null) { - break; - } + long scalarEmission = 0; + U o = null; + while (r != 0L) { + o = svq.poll(); - child.onNext(o); - - replenishMain++; - scalarEmission++; - r--; - } - if (scalarEmission != 0L) { - if (unbounded) { - r = Long.MAX_VALUE; - } else { - r = requested.addAndGet(-scalarEmission); - } + if (checkTerminate()) { + return; } - if (r == 0L || o == null) { + if (o == null) { break; } + + child.onNext(o); + + replenishMain++; + scalarEmission++; + r--; + } + if (scalarEmission != 0L) { + if (unbounded) { + r = Long.MAX_VALUE; + } else { + r = requested.addAndGet(-scalarEmission); + } } } @@ -457,15 +446,15 @@ void drainLoop() { U o = null; for (;;) { - if (checkTerminate()) { - return; - } SimpleQueue q = is.queue; if (q == null) { break; } long produced = 0; while (r != 0L) { + if (checkTerminate()) { + return; + } try { o = q.poll(); @@ -490,10 +479,6 @@ void drainLoop() { child.onNext(o); - if (checkTerminate()) { - return; - } - r--; produced++; } @@ -566,15 +551,12 @@ void clearScalarQueue() { } void disposeAll() { - InnerSubscriber[] a = subscribers.get(); + InnerSubscriber[] a = subscribers.getAndSet(CANCELLED); if (a != CANCELLED) { - a = subscribers.getAndSet(CANCELLED); - if (a != CANCELLED) { - for (InnerSubscriber inner : a) { - inner.dispose(); - } - errors.tryTerminateAndReport(); + for (InnerSubscriber inner : a) { + inner.dispose(); } + errors.tryTerminateAndReport(); } } @@ -606,10 +588,10 @@ static final class InnerSubscriber extends AtomicReference long produced; int fusionMode; - InnerSubscriber(MergeSubscriber parent, long id) { + InnerSubscriber(MergeSubscriber parent, int bufferSize, long id) { this.id = id; this.parent = parent; - this.bufferSize = parent.bufferSize; + this.bufferSize = bufferSize; this.limit = bufferSize >> 2; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java index 5c20e9d75a..b69a6e2895 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import org.reactivestreams.*; @@ -23,7 +24,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; @@ -50,7 +50,7 @@ public FlowableFlatMapCompletable(Flowable source, @Override protected void subscribeActual(Subscriber subscriber) { - source.subscribe(new FlatMapCompletableMainSubscriber(subscriber, mapper, delayErrors, maxConcurrency)); + source.subscribe(new FlatMapCompletableMainSubscriber<>(subscriber, mapper, delayErrors, maxConcurrency)); } static final class FlatMapCompletableMainSubscriber extends BasicIntQueueSubscription @@ -106,7 +106,7 @@ public void onNext(T value) { CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -138,9 +138,7 @@ public void onError(Throwable e) { cancelled = true; upstream.cancel(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } @@ -171,7 +169,7 @@ public void request(long n) { @Nullable @Override - public T poll() throws Exception { + public T poll() { return null; // always empty } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java index 52a719ca44..2d604e5157 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.Subscription; @@ -22,7 +23,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.FuseToFlowable; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.AtomicThrowable; @@ -53,12 +53,12 @@ public FlowableFlatMapCompletableCompletable(Flowable source, @Override protected void subscribeActual(CompletableObserver observer) { - source.subscribe(new FlatMapCompletableMainSubscriber(observer, mapper, delayErrors, maxConcurrency)); + source.subscribe(new FlatMapCompletableMainSubscriber<>(observer, mapper, delayErrors, maxConcurrency)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableFlatMapCompletable(source, mapper, delayErrors, maxConcurrency)); + return RxJavaPlugins.onAssembly(new FlowableFlatMapCompletable<>(source, mapper, delayErrors, maxConcurrency)); } static final class FlatMapCompletableMainSubscriber extends AtomicInteger @@ -114,7 +114,7 @@ public void onNext(T value) { CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -146,9 +146,7 @@ public void onError(Throwable e) { disposed = true; upstream.cancel(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java index f6cb69c84a..2936fa9066 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,10 +23,9 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; /** * Maps upstream values into MaybeSources and merges their signals into one sequence. @@ -50,7 +50,7 @@ public FlowableFlatMapMaybe(Flowable source, Function s) { - source.subscribe(new FlatMapMaybeSubscriber(s, mapper, delayErrors, maxConcurrency)); + source.subscribe(new FlatMapMaybeSubscriber<>(s, mapper, delayErrors, maxConcurrency)); } static final class FlatMapMaybeSubscriber @@ -91,7 +91,7 @@ static final class FlatMapMaybeSubscriber this.set = new CompositeDisposable(); this.errors = new AtomicThrowable(); this.active = new AtomicInteger(1); - this.queue = new AtomicReference>(); + this.queue = new AtomicReference<>(); } @Override @@ -115,7 +115,7 @@ public void onNext(T t) { MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -174,7 +174,7 @@ void innerSuccess(InnerObserver inner, R value) { SpscLinkedArrayQueue q = queue.get(); - if (d && (q == null || q.isEmpty())) { + if (checkTerminate(d, q)) { errors.tryTerminateConsumer(downstream); return; } @@ -205,16 +205,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue(Flowable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; } + current = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; + } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { @@ -240,7 +239,7 @@ void innerComplete(InnerObserver inner) { boolean d = active.decrementAndGet() == 0; SpscLinkedArrayQueue q = queue.get(); - if (d && (q == null || q.isEmpty())) { + if (checkTerminate(d, q)) { errors.tryTerminateConsumer(downstream); return; } @@ -261,6 +260,10 @@ void innerComplete(InnerObserver inner) { } } + static boolean checkTerminate(boolean d, SpscLinkedArrayQueue q) { + return d && (q == null || q.isEmpty()); + } + void drain() { if (getAndIncrement() == 0) { drainLoop(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybePublisher.java similarity index 53% rename from src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapPublisher.java rename to src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybePublisher.java index 0f5be8917d..acc527215c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybePublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,31 +15,35 @@ import org.reactivestreams.*; -import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableFlatMapMaybe.FlatMapMaybeSubscriber; + +/** + * Maps upstream values into MaybeSources and merges their signals into one sequence. + * @param the source value type + * @param the result value type + */ +public final class FlowableFlatMapMaybePublisher extends Flowable { -public final class FlowableFlatMapPublisher extends Flowable { final Publisher source; - final Function> mapper; + + final Function> mapper; + final boolean delayErrors; + final int maxConcurrency; - final int bufferSize; - public FlowableFlatMapPublisher(Publisher source, - Function> mapper, - boolean delayErrors, int maxConcurrency, int bufferSize) { + public FlowableFlatMapMaybePublisher(Publisher source, Function> mapper, + boolean delayError, int maxConcurrency) { this.source = source; this.mapper = mapper; - this.delayErrors = delayErrors; + this.delayErrors = delayError; this.maxConcurrency = maxConcurrency; - this.bufferSize = bufferSize; } @Override - protected void subscribeActual(Subscriber s) { - if (FlowableScalarXMap.tryScalarXMapSubscribe(source, s, mapper)) { - return; - } - source.subscribe(FlowableFlatMap.subscribe(s, mapper, delayErrors, maxConcurrency, bufferSize)); + protected void subscribeActual(Subscriber s) { + source.subscribe(new FlatMapMaybeSubscriber<>(s, mapper, delayErrors, maxConcurrency)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java index b1ba1fed12..f5277d603a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,10 +23,9 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; /** * Maps upstream values into SingleSources and merges their signals into one sequence. @@ -50,7 +50,7 @@ public FlowableFlatMapSingle(Flowable source, Function s) { - source.subscribe(new FlatMapSingleSubscriber(s, mapper, delayErrors, maxConcurrency)); + source.subscribe(new FlatMapSingleSubscriber<>(s, mapper, delayErrors, maxConcurrency)); } static final class FlatMapSingleSubscriber @@ -91,7 +91,7 @@ static final class FlatMapSingleSubscriber this.set = new CompositeDisposable(); this.errors = new AtomicThrowable(); this.active = new AtomicInteger(1); - this.queue = new AtomicReference>(); + this.queue = new AtomicReference<>(); } @Override @@ -115,7 +115,7 @@ public void onNext(T t) { SingleSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); + ms = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -205,16 +205,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue(Flowable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; + } + current = new SpscLinkedArrayQueue<>(Flowable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSinglePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSinglePublisher.java new file mode 100644 index 0000000000..570ef96f78 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSinglePublisher.java @@ -0,0 +1,49 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableFlatMapSingle.FlatMapSingleSubscriber; + +/** + * Maps upstream values into SingleSources and merges their signals into one sequence. + * @param the source value type + * @param the result value type + */ +public final class FlowableFlatMapSinglePublisher extends Flowable { + + final Publisher source; + + final Function> mapper; + + final boolean delayErrors; + + final int maxConcurrency; + + public FlowableFlatMapSinglePublisher(Publisher source, Function> mapper, + boolean delayError, int maxConcurrency) { + this.source = source; + this.mapper = mapper; + this.delayErrors = delayError; + this.maxConcurrency = maxConcurrency; + } + + @Override + protected void subscribeActual(Subscriber s) { + source.subscribe(new FlatMapSingleSubscriber<>(s, mapper, delayErrors, maxConcurrency)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterable.java index 808d964798..0b9164e0cd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,7 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; -import java.util.Iterator; +import java.util.*; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,11 +22,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableFlattenIterable extends AbstractFlowableWithUpstream { @@ -77,7 +77,20 @@ public void subscribeActual(Subscriber s) { return; } - source.subscribe(new FlattenIterableSubscriber(s, mapper, prefetch)); + source.subscribe(new FlattenIterableSubscriber<>(s, mapper, prefetch)); + } + + /** + * Create a {@link Subscriber} with the given parameters. + * @param the upstream value type + * @param the {@link Iterable} and output value type + * @param downstream the downstream {@code Subscriber} to wrap + * @param mapper the mapper function + * @param prefetch the number of items to prefetch + * @return the new {@code Subscriber} + */ + public static Subscriber subscribe(Subscriber downstream, Function> mapper, int prefetch) { + return new FlattenIterableSubscriber<>(downstream, mapper, prefetch); } static final class FlattenIterableSubscriber @@ -118,7 +131,7 @@ static final class FlattenIterableSubscriber this.mapper = mapper; this.prefetch = prefetch; this.limit = prefetch - (prefetch >> 2); - this.error = new AtomicReference(); + this.error = new AtomicReference<>(); this.requested = new AtomicLong(); } @@ -153,7 +166,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); downstream.onSubscribe(this); @@ -167,7 +180,7 @@ public void onNext(T t) { return; } if (fusionMode == NONE && !queue.offer(t)) { - onError(new MissingBackpressureException("Queue is full?!")); + onError(new QueueOverflowException()); return; } drain(); @@ -297,7 +310,7 @@ void drain() { R v; try { - v = ObjectHelper.requireNonNull(it.next(), "The iterator returned a null value"); + v = Objects.requireNonNull(it.next(), "The iterator returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); current = null; @@ -432,7 +445,7 @@ public R poll() throws Throwable { current = it; } - R r = ObjectHelper.requireNonNull(it.next(), "The iterator returned a null value"); + R r = Objects.requireNonNull(it.next(), "The iterator returned a null value"); if (!it.hasNext()) { current = null; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromAction.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromAction.java new file mode 100644 index 0000000000..693f40ddb4 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromAction.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.fuseable.CancellableQueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Executes an {@link Action} and signals its exception or completes normally. + * + * @param the value type + * @since 3.0.0 + */ +public final class FlowableFromAction extends Flowable implements Supplier { + + final Action action; + + public FlowableFromAction(Action action) { + this.action = action; + } + + @Override + protected void subscribeActual(Subscriber subscriber) { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + subscriber.onSubscribe(qs); + + if (!qs.isDisposed()) { + + try { + action.run(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + if (!qs.isDisposed()) { + subscriber.onError(ex); + } else { + RxJavaPlugins.onError(ex); + } + return; + } + + if (!qs.isDisposed()) { + subscriber.onComplete(); + } + } + } + + @Override + public T get() throws Throwable { + action.run(); + return null; // considered as onComplete() + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArray.java index 0b49186290..1b13bcff3b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,11 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; + +import java.util.Objects; public final class FlowableFromArray extends Flowable { final T[] array; @@ -32,10 +33,10 @@ public FlowableFromArray(T[] array) { @Override public void subscribeActual(Subscriber s) { if (s instanceof ConditionalSubscriber) { - s.onSubscribe(new ArrayConditionalSubscription( + s.onSubscribe(new ArrayConditionalSubscription<>( (ConditionalSubscriber)s, array)); } else { - s.onSubscribe(new ArraySubscription(s, array)); + s.onSubscribe(new ArraySubscription<>(s, array)); } } @@ -67,7 +68,7 @@ public final T poll() { } index = i + 1; - return ObjectHelper.requireNonNull(arr[i], "array element is null"); + return Objects.requireNonNull(arr[i], "array element is null"); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallable.java index 7f918c5c51..e2a331d2f0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.Callable; import org.reactivestreams.Subscriber; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.DeferredScalarSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -32,12 +32,12 @@ public FlowableFromCallable(Callable callable) { @Override public void subscribeActual(Subscriber s) { - DeferredScalarSubscription deferred = new DeferredScalarSubscription(s); + DeferredScalarSubscription deferred = new DeferredScalarSubscription<>(s); s.onSubscribe(deferred); T t; try { - t = ObjectHelper.requireNonNull(callable.call(), "The callable returned a null value"); + t = Objects.requireNonNull(callable.call(), "The callable returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (deferred.isCancelled()) { @@ -53,6 +53,6 @@ public void subscribeActual(Subscriber s) { @Override public T get() throws Throwable { - return ObjectHelper.requireNonNull(callable.call(), "The callable returned a null value"); + return Objects.requireNonNull(callable.call(), "The callable returned a null value"); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletable.java new file mode 100644 index 0000000000..c4b15b947d --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletable.java @@ -0,0 +1,86 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.fuseable.*; + +/** + * Wrap a Completable into a Flowable. + * + * @param the value type + * @since 3.0.0 + */ +public final class FlowableFromCompletable extends Flowable implements HasUpstreamCompletableSource { + + final CompletableSource source; + + public FlowableFromCompletable(CompletableSource source) { + this.source = source; + } + + @Override + public CompletableSource source() { + return source; + } + + @Override + protected void subscribeActual(Subscriber observer) { + source.subscribe(new FromCompletableObserver(observer)); + } + + public static final class FromCompletableObserver + extends AbstractEmptyQueueFuseable + implements CompletableObserver { + + final Subscriber downstream; + + Disposable upstream; + + public FromCompletableObserver(Subscriber downstream) { + this.downstream = downstream; + } + + @Override + public void cancel() { + upstream.dispose(); + upstream = DisposableHelper.DISPOSED; + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onComplete() { + upstream = DisposableHelper.DISPOSED; + downstream.onComplete(); + } + + @Override + public void onError(Throwable e) { + upstream = DisposableHelper.DISPOSED; + downstream.onError(e); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture.java index 8356c92915..0fef368287 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public FlowableFromFuture(Future future, long timeout, TimeUnit uni @Override public void subscribeActual(Subscriber s) { - DeferredScalarSubscription deferred = new DeferredScalarSubscription(s); + DeferredScalarSubscription deferred = new DeferredScalarSubscription<>(s); s.onSubscribe(deferred); T v; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java index c8dc87c0c3..314f0f2f54 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,16 +14,16 @@ package io.reactivex.rxjava3.internal.operators.flowable; import java.util.Iterator; +import java.util.Objects; import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; public final class FlowableFromIterable extends Flowable { @@ -73,14 +73,14 @@ public static void subscribe(Subscriber s, Iterator abstract static class BaseRangeSubscription extends BasicQueueSubscription { private static final long serialVersionUID = -2252972430506210021L; - Iterator it; + Iterator iterator; volatile boolean cancelled; boolean once; BaseRangeSubscription(Iterator it) { - this.it = it; + this.iterator = it; } @Override @@ -91,27 +91,34 @@ public final int requestFusion(int mode) { @Nullable @Override public final T poll() { - if (it == null) { + if (iterator == null) { return null; } if (!once) { once = true; } else { - if (!it.hasNext()) { + if (!iterator.hasNext()) { return null; } } - return ObjectHelper.requireNonNull(it.next(), "Iterator.next() returned a null value"); + return Objects.requireNonNull(iterator.next(), "Iterator.next() returned a null value"); } @Override public final boolean isEmpty() { - return it == null || !it.hasNext(); + Iterator it = this.iterator; + if (it != null) { + if (!once || it.hasNext()) { + return false; + } + clear(); + } + return true; } @Override public final void clear() { - it = null; + iterator = null; } @Override @@ -150,7 +157,7 @@ static final class IteratorSubscription extends BaseRangeSubscription { @Override void fastPath() { - Iterator it = this.it; + Iterator it = this.iterator; Subscriber a = downstream; for (;;) { if (cancelled) { @@ -204,7 +211,7 @@ void fastPath() { @Override void slowPath(long r) { long e = 0L; - Iterator it = this.it; + Iterator it = this.iterator; Subscriber a = downstream; for (;;) { @@ -286,7 +293,7 @@ static final class IteratorConditionalSubscription extends BaseRangeSubscript @Override void fastPath() { - Iterator it = this.it; + Iterator it = this.iterator; ConditionalSubscriber a = downstream; for (;;) { if (cancelled) { @@ -340,7 +347,7 @@ void fastPath() { @Override void slowPath(long r) { long e = 0L; - Iterator it = this.it; + Iterator it = this.iterator; ConditionalSubscriber a = downstream; for (;;) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable.java index e88fd35c73..664dbf4acf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.*; @@ -18,15 +19,16 @@ import io.reactivex.rxjava3.disposables.Disposable; public final class FlowableFromObservable extends Flowable { - private final Observable upstream; - public FlowableFromObservable(Observable upstream) { + private final ObservableSource upstream; + + public FlowableFromObservable(ObservableSource upstream) { this.upstream = upstream; } @Override protected void subscribeActual(Subscriber s) { - upstream.subscribe(new SubscriberObserver(s)); + upstream.subscribe(new SubscriberObserver<>(s)); } static final class SubscriberObserver implements Observer, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromPublisher.java index df575a0274..a021846388 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromRunnable.java new file mode 100644 index 0000000000..0d503d4668 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromRunnable.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Supplier; +import io.reactivex.rxjava3.internal.fuseable.CancellableQueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Executes an {@link Runnable} and signals its exception or completes normally. + * + * @param the value type + * @since 3.0.0 + */ +public final class FlowableFromRunnable extends Flowable implements Supplier { + + final Runnable run; + + public FlowableFromRunnable(Runnable run) { + this.run = run; + } + + @Override + protected void subscribeActual(Subscriber subscriber) { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + subscriber.onSubscribe(qs); + + if (!qs.isDisposed()) { + + try { + run.run(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + if (!qs.isDisposed()) { + subscriber.onError(ex); + } else { + RxJavaPlugins.onError(ex); + } + return; + } + + if (!qs.isDisposed()) { + subscriber.onComplete(); + } + } + } + + @Override + public T get() throws Throwable { + run.run(); + return null; // considered as onComplete() + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplier.java index bd714ee6c0..ca2a331396 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.DeferredScalarSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Call a Supplier for each incoming Subscriber and signal the returned value or the thrown exception. * @param the value type and element type returned by the supplier and the flow @@ -37,12 +38,12 @@ public FlowableFromSupplier(Supplier supplier) { @Override public void subscribeActual(Subscriber s) { - DeferredScalarSubscription deferred = new DeferredScalarSubscription(s); + DeferredScalarSubscription deferred = new DeferredScalarSubscription<>(s); s.onSubscribe(deferred); T t; try { - t = ObjectHelper.requireNonNull(supplier.get(), "The supplier returned a null value"); + t = Objects.requireNonNull(supplier.get(), "The supplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (deferred.isCancelled()) { @@ -58,6 +59,6 @@ public void subscribeActual(Subscriber s) { @Override public T get() throws Throwable { - return ObjectHelper.requireNonNull(supplier.get(), "The supplier returned a null value"); + return Objects.requireNonNull(supplier.get(), "The supplier returned a null value"); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerate.java index 893ec19ff9..3639d64eeb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,7 +48,7 @@ public void subscribeActual(Subscriber s) { return; } - s.onSubscribe(new GeneratorSubscription(s, generator, disposeState, state)); + s.onSubscribe(new GeneratorSubscription<>(s, generator, disposeState, state)); } static final class GeneratorSubscription diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java index 85c7d08289..50e641ffc1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupBy.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,9 +24,9 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.flowables.GroupedFlowable; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableGroupBy extends AbstractFlowableWithUpstream> { @@ -56,10 +56,10 @@ protected void subscribeActual(Subscriber> s) { try { if (mapFactory == null) { evictedGroups = null; - groups = new ConcurrentHashMap>(); + groups = new ConcurrentHashMap<>(); } else { - evictedGroups = new ConcurrentLinkedQueue>(); - Consumer evictionAction = (Consumer) new EvictionAction(evictedGroups); + evictedGroups = new ConcurrentLinkedQueue<>(); + Consumer evictionAction = (Consumer) new EvictionAction<>(evictedGroups); groups = (Map) mapFactory.apply(evictionAction); } } catch (Throwable e) { @@ -69,7 +69,7 @@ protected void subscribeActual(Subscriber> s) { return; } GroupBySubscriber subscriber = - new GroupBySubscriber(s, keySelector, valueSelector, bufferSize, delayError, groups, evictedGroups); + new GroupBySubscriber<>(s, keySelector, valueSelector, bufferSize, delayError, groups, evictedGroups); source.subscribe(subscriber); } @@ -168,7 +168,7 @@ public void onNext(T t) { if (emittedGroups != get()) { downstream.onNext(group); } else { - MissingBackpressureException mbe = new MissingBackpressureException(groupHangWarning(emittedGroups)); + MissingBackpressureException mbe = groupHangWarning(emittedGroups); mbe.initCause(ex); onError(mbe); return; @@ -194,13 +194,13 @@ public void onNext(T t) { } } else { upstream.cancel(); - onError(new MissingBackpressureException(groupHangWarning(emittedGroups))); + onError(groupHangWarning(emittedGroups)); } } } - static String groupHangWarning(long n) { - return "Unable to emit a new group (#" + n + ") due to lack of requests. Please make sure the downstream can always accept a new group as well as each group is consumed in order for the whole operator to be able to proceed."; + static MissingBackpressureException groupHangWarning(long n) { + return new MissingBackpressureException("Unable to emit a new group (#" + n + ") due to lack of requests. Please make sure the downstream can always accept a new group as well as each group is consumed in order for the whole operator to be able to proceed."); } @Override @@ -214,9 +214,7 @@ public void onError(Throwable t) { g.onError(t); } groups.clear(); - if (evictedGroups != null) { - evictedGroups.clear(); - } + completeEvictions(); downstream.onError(t); } @@ -226,10 +224,10 @@ public void onComplete() { for (GroupedUnicast g : groups.values()) { g.onComplete(); } + groups.clear(); - if (evictedGroups != null) { - evictedGroups.clear(); - } + completeEvictions(); + done = true; downstream.onComplete(); } @@ -259,8 +257,9 @@ private void completeEvictions() { int count = 0; GroupedUnicast evictedGroup; while ((evictedGroup = evictedGroups.poll()) != null) { - evictedGroup.onComplete(); - count++; + if (evictedGroup.state.tryComplete()) { + count++; + } } if (count != 0) { groupCount.addAndGet(-count); @@ -270,9 +269,10 @@ private void completeEvictions() { public void cancel(K key) { Object mapKey = key != null ? key : NULL_KEY; - groups.remove(mapKey); - if (groupCount.decrementAndGet() == 0) { - upstream.cancel(); + if (groups.remove(mapKey) != null) { + if (groupCount.decrementAndGet() == 0) { + upstream.cancel(); + } } } @@ -327,8 +327,8 @@ static final class GroupedUnicast extends GroupedFlowable { final State state; public static GroupedUnicast createWith(K key, int bufferSize, GroupBySubscriber parent, boolean delayError) { - State state = new State(bufferSize, parent, key, delayError); - return new GroupedUnicast(key, state); + State state = new State<>(bufferSize, parent, key, delayError); + return new GroupedUnicast<>(key, state); } protected GroupedUnicast(K key, State state) { @@ -370,7 +370,7 @@ static final class State extends BasicIntQueueSubscription implements P final AtomicBoolean cancelled = new AtomicBoolean(); - final AtomicReference> actual = new AtomicReference>(); + final AtomicReference> actual = new AtomicReference<>(); boolean outputFused; int produced; @@ -382,8 +382,10 @@ static final class State extends BasicIntQueueSubscription implements P static final int ABANDONED = 2; static final int ABANDONED_HAS_SUBSCRIBER = ABANDONED | HAS_SUBSCRIBER; + final AtomicBoolean evictOnce = new AtomicBoolean(); + State(int bufferSize, GroupBySubscriber parent, K key, boolean delayError) { - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.parent = parent; this.key = key; this.delayError = delayError; @@ -401,6 +403,7 @@ public void request(long n) { public void cancel() { if (cancelled.compareAndSet(false, true)) { cancelParent(); + drain(); } } @@ -442,9 +445,18 @@ public void onComplete() { drain(); } + boolean tryComplete() { + boolean canEvict = evictOnce.compareAndSet(false, true); + done = true; + drain(); + return canEvict; + } + void cancelParent() { if ((once.get() & ABANDONED) == 0) { - parent.cancel(key); + if (evictOnce.compareAndSet(false, true)) { + parent.cancel(key); + } } } @@ -516,37 +528,44 @@ void drainNormal() { final SpscLinkedArrayQueue q = queue; final boolean delayError = this.delayError; Subscriber a = actual.get(); + final AtomicBoolean cancelled = this.cancelled; + + outer: for (;;) { - if (a != null) { - long r = requested.get(); - long e = 0; + if (cancelled.get()) { + cleanupQueue(0, false); + } else { + if (a != null) { + long r = requested.get(); + long e = 0; - while (e != r) { - boolean d = done; - T v = q.poll(); - boolean empty = v == null; + while (e != r) { + boolean d = done; + T v = q.poll(); + boolean empty = v == null; - if (checkTerminated(d, empty, a, delayError, e)) { - return; - } + if (checkTerminated(d, empty, a, delayError, e, !empty)) { + continue outer; + } - if (empty) { - break; - } + if (empty) { + break; + } - a.onNext(v); + a.onNext(v); - e++; - } + e++; + } - if (e == r && checkTerminated(done, q.isEmpty(), a, delayError, e)) { - return; - } + if (e == r && checkTerminated(done, q.isEmpty(), a, delayError, e, false)) { + continue outer; + } - if (e != 0L) { - BackpressureHelper.produced(requested, e); - // replenish based on this batch run - requestParent(e); + if (e != 0L) { + BackpressureHelper.produced(requested, e); + // replenish based on this batch run + requestParent(e); + } } } @@ -566,28 +585,45 @@ void requestParent(long e) { } } - boolean checkTerminated(boolean d, boolean empty, Subscriber a, boolean delayError, long emitted) { + void cleanupQueue(long emitted, boolean polled) { + // if this group is canceled, all accumulated emissions and + // remaining items in the queue should be requested + // so that other groups can proceed + while (queue.poll() != null) { + emitted++; + } + + replenishParent(emitted, polled); + } + + void replenishParent(long emitted, boolean polled) { + if (polled) { + emitted++; + } + if (emitted != 0L) { + requestParent(emitted); + } + } + + boolean checkTerminated(boolean d, boolean empty, Subscriber a, + boolean delayError, long emitted, boolean polled) { if (cancelled.get()) { - // if this group is canceled, all accumulated emissions and - // remaining items in the queue should be requested - // so that other groups can proceed - while (queue.poll() != null) { - emitted++; - } - if (emitted != 0L) { - requestParent(emitted); - } + cleanupQueue(emitted, polled); return true; } if (d) { if (delayError) { if (empty) { + cancelled.lazySet(true); Throwable e = error; if (e != null) { a.onError(e); } else { a.onComplete(); + // completion doesn't mean the parent has completed + // because of evicted groups + replenishParent(emitted, polled); } return true; } @@ -595,11 +631,17 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, boole Throwable e = error; if (e != null) { queue.clear(); + cancelled.lazySet(true); a.onError(e); return true; } else if (empty) { + cancelled.lazySet(true); a.onComplete(); + + // completion doesn't mean the parent has completed + // because of evicted groups + replenishParent(emitted, polled); return true; } } @@ -610,10 +652,13 @@ boolean checkTerminated(boolean d, boolean empty, Subscriber a, boole @Override public int requestFusion(int mode) { + // FIXME fusion mode causes hangs + /* if ((mode & ASYNC) != 0) { outputFused = true; return ASYNC; } + */ return NONE; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java index 213073c672..c9619c48b7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoin.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; @@ -25,11 +22,10 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.UnicastProcessor; @@ -60,7 +56,7 @@ public FlowableGroupJoin( protected void subscribeActual(Subscriber s) { GroupJoinSubscription parent = - new GroupJoinSubscription(s, leftEnd, rightEnd, resultSelector); + new GroupJoinSubscription<>(s, leftEnd, rightEnd, resultSelector); s.onSubscribe(parent); @@ -133,10 +129,10 @@ static final class GroupJoinSubscription this.downstream = actual; this.requested = new AtomicLong(); this.disposables = new CompositeDisposable(); - this.queue = new SpscLinkedArrayQueue(bufferSize()); - this.lefts = new LinkedHashMap>(); - this.rights = new LinkedHashMap(); - this.error = new AtomicReference(); + this.queue = new SpscLinkedArrayQueue<>(bufferSize()); + this.lefts = new LinkedHashMap<>(); + this.rights = new LinkedHashMap<>(); + this.error = new AtomicReference<>(); this.leftEnd = leftEnd; this.rightEnd = rightEnd; this.resultSelector = resultSelector; @@ -240,14 +236,14 @@ void drain() { @SuppressWarnings("unchecked") TLeft left = (TLeft)val; - UnicastProcessor up = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); int idx = leftIndex++; lefts.put(idx, up); Publisher p; try { - p = ObjectHelper.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null Publisher"); + p = Objects.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null Publisher"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -269,7 +265,7 @@ void drain() { R w; try { - w = ObjectHelper.requireNonNull(resultSelector.apply(left, up), "The resultSelector returned a null value"); + w = Objects.requireNonNull(resultSelector.apply(left, up), "The resultSelector returned a null value"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -280,7 +276,7 @@ void drain() { a.onNext(w); BackpressureHelper.produced(requested, 1); } else { - fail(new MissingBackpressureException("Could not emit value due to lack of requests"), a, q); + fail(MissingBackpressureException.createDefault(), a, q); return; } @@ -299,7 +295,7 @@ else if (mode == RIGHT_VALUE) { Publisher p; try { - p = ObjectHelper.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null Publisher"); + p = Objects.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null Publisher"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -331,7 +327,7 @@ else if (mode == LEFT_CLOSE) { up.onComplete(); } } - else if (mode == RIGHT_CLOSE) { + else { LeftRightEndSubscriber end = (LeftRightEndSubscriber)val; rights.remove(end.index); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHide.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHide.java index 512269f58f..bc9b47011b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHide.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHide.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public FlowableHide(Flowable source) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new HideSubscriber(s)); + source.subscribe(new HideSubscriber<>(s)); } static final class HideSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElements.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElements.java index c30935c569..20f546feee 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElements.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElements.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,8 +17,8 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.QueueSubscription; public final class FlowableIgnoreElements extends AbstractFlowableWithUpstream { @@ -28,7 +28,7 @@ public FlowableIgnoreElements(Flowable source) { @Override protected void subscribeActual(final Subscriber t) { - source.subscribe(new IgnoreElementsSubscriber(t)); + source.subscribe(new IgnoreElementsSubscriber<>(t)); } static final class IgnoreElementsSubscriber implements FlowableSubscriber, QueueSubscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsCompletable.java index e68e5dd784..19d8546d2d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,12 +31,12 @@ public FlowableIgnoreElementsCompletable(Flowable source) { @Override protected void subscribeActual(final CompletableObserver t) { - source.subscribe(new IgnoreElementsSubscriber(t)); + source.subscribe(new IgnoreElementsSubscriber<>(t)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableIgnoreElements(source)); + return RxJavaPlugins.onAssembly(new FlowableIgnoreElements<>(source)); } static final class IgnoreElementsSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelper.java index 243d58b9b8..781d475872 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,10 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.TimeUnit; import org.reactivestreams.*; @@ -46,7 +48,7 @@ public S apply(S t1, Emitter t2) throws Throwable { } public static BiFunction, S> simpleGenerator(Consumer> consumer) { - return new SimpleGenerator(consumer); + return new SimpleGenerator<>(consumer); } static final class SimpleBiGenerator implements BiFunction, S> { @@ -64,7 +66,7 @@ public S apply(S t1, Emitter t2) throws Throwable { } public static BiFunction, S> simpleBiGenerator(BiConsumer> consumer) { - return new SimpleBiGenerator(consumer); + return new SimpleBiGenerator<>(consumer); } static final class ItemDelayFunction implements Function> { @@ -76,13 +78,13 @@ static final class ItemDelayFunction implements Function> @Override public Publisher apply(final T v) throws Throwable { - Publisher p = ObjectHelper.requireNonNull(itemDelay.apply(v), "The itemDelay returned a null Publisher"); - return new FlowableTakePublisher(p, 1).map(Functions.justFunction(v)).defaultIfEmpty(v); + Publisher p = Objects.requireNonNull(itemDelay.apply(v), "The itemDelay returned a null Publisher"); + return new FlowableTakePublisher<>(p, 1).map(Functions.justFunction(v)).defaultIfEmpty(v); } } public static Function> itemDelay(final Function> itemDelay) { - return new ItemDelayFunction(itemDelay); + return new ItemDelayFunction<>(itemDelay); } static final class SubscriberOnNext implements Consumer { @@ -93,7 +95,7 @@ static final class SubscriberOnNext implements Consumer { } @Override - public void accept(T v) throws Exception { + public void accept(T v) { subscriber.onNext(v); } } @@ -106,7 +108,7 @@ static final class SubscriberOnError implements Consumer { } @Override - public void accept(Throwable v) throws Exception { + public void accept(Throwable v) { subscriber.onError(v); } } @@ -119,21 +121,21 @@ static final class SubscriberOnComplete implements Action { } @Override - public void run() throws Exception { + public void run() { subscriber.onComplete(); } } public static Consumer subscriberOnNext(Subscriber subscriber) { - return new SubscriberOnNext(subscriber); + return new SubscriberOnNext<>(subscriber); } public static Consumer subscriberOnError(Subscriber subscriber) { - return new SubscriberOnError(subscriber); + return new SubscriberOnError<>(subscriber); } public static Action subscriberOnComplete(Subscriber subscriber) { - return new SubscriberOnComplete(subscriber); + return new SubscriberOnComplete<>(subscriber); } static final class FlatMapWithCombinerInner implements Function { @@ -164,15 +166,15 @@ static final class FlatMapWithCombinerOuter implements Function apply(final T t) throws Throwable { @SuppressWarnings("unchecked") - Publisher u = (Publisher)ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); - return new FlowableMapPublisher(u, new FlatMapWithCombinerInner(combiner, t)); + Publisher u = (Publisher)Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + return new FlowableMapPublisher<>(u, new FlatMapWithCombinerInner(combiner, t)); } } public static Function> flatMapWithCombiner( final Function> mapper, final BiFunction combiner) { - return new FlatMapWithCombinerOuter(combiner, mapper); + return new FlatMapWithCombinerOuter<>(combiner, mapper); } static final class FlatMapIntoIterable implements Function> { @@ -184,34 +186,34 @@ static final class FlatMapIntoIterable implements Function @Override public Publisher apply(T t) throws Throwable { - return new FlowableFromIterable(ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Iterable")); + return new FlowableFromIterable<>(Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Iterable")); } } public static Function> flatMapIntoIterable(final Function> mapper) { - return new FlatMapIntoIterable(mapper); + return new FlatMapIntoIterable<>(mapper); } public static Supplier> replaySupplier(final Flowable parent) { - return new ReplaySupplier(parent); + return new ReplaySupplier<>(parent); } public static Supplier> replaySupplier(final Flowable parent, final int bufferSize, boolean eagerTruncate) { - return new BufferedReplaySupplier(parent, bufferSize, eagerTruncate); + return new BufferedReplaySupplier<>(parent, bufferSize, eagerTruncate); } public static Supplier> replaySupplier(final Flowable parent, final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - return new BufferedTimedReplay(parent, bufferSize, time, unit, scheduler, eagerTruncate); + return new BufferedTimedReplay<>(parent, bufferSize, time, unit, scheduler, eagerTruncate); } public static Supplier> replaySupplier(final Flowable parent, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - return new TimedReplay(parent, time, unit, scheduler, eagerTruncate); + return new TimedReplay<>(parent, time, unit, scheduler, eagerTruncate); } public enum RequestMax implements Consumer { INSTANCE; @Override - public void accept(Subscription t) throws Exception { + public void accept(Subscription t) { t.request(Long.MAX_VALUE); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInterval.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInterval.java index 44e735be37..98fbe6dee1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInterval.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInterval.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -66,7 +66,7 @@ static final class IntervalSubscriber extends AtomicLong long count; - final AtomicReference resource = new AtomicReference(); + final AtomicReference resource = new AtomicReference<>(); IntervalSubscriber(Subscriber downstream) { this.downstream = downstream; @@ -93,7 +93,7 @@ public void run() { downstream.onNext(count++); BackpressureHelper.produced(this, 1); } else { - downstream.onError(new MissingBackpressureException("Can't deliver value " + count + " due to lack of requests")); + downstream.onError(new MissingBackpressureException("Could not emit value " + count + " due to lack of requests")); DisposableHelper.dispose(resource); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRange.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRange.java index 83ff94ecaa..cfa605882d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRange.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRange.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -71,7 +71,7 @@ static final class IntervalRangeSubscriber extends AtomicLong long count; - final AtomicReference resource = new AtomicReference(); + final AtomicReference resource = new AtomicReference<>(); IntervalRangeSubscriber(Subscriber actual, long start, long end) { this.downstream = actual; @@ -114,7 +114,7 @@ public void run() { decrementAndGet(); } } else { - downstream.onError(new MissingBackpressureException("Can't deliver value " + count + " due to lack of requests")); + downstream.onError(new MissingBackpressureException("Could not emit value " + count + " due to lack of requests")); DisposableHelper.dispose(resource); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java index 591cdb159a..9cda3d457d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoin.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,12 +22,11 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.operators.flowable.FlowableGroupJoin.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableJoin extends AbstractFlowableWithUpstream { @@ -57,7 +56,7 @@ public FlowableJoin( protected void subscribeActual(Subscriber s) { JoinSubscription parent = - new JoinSubscription(s, leftEnd, rightEnd, resultSelector); + new JoinSubscription<>(s, leftEnd, rightEnd, resultSelector); s.onSubscribe(parent); @@ -117,10 +116,10 @@ static final class JoinSubscription this.downstream = actual; this.requested = new AtomicLong(); this.disposables = new CompositeDisposable(); - this.queue = new SpscLinkedArrayQueue(bufferSize()); - this.lefts = new LinkedHashMap(); - this.rights = new LinkedHashMap(); - this.error = new AtomicReference(); + this.queue = new SpscLinkedArrayQueue<>(bufferSize()); + this.lefts = new LinkedHashMap<>(); + this.rights = new LinkedHashMap<>(); + this.error = new AtomicReference<>(); this.leftEnd = leftEnd; this.rightEnd = rightEnd; this.resultSelector = resultSelector; @@ -223,7 +222,7 @@ void drain() { Publisher p; try { - p = ObjectHelper.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null Publisher"); + p = Objects.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null Publisher"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -250,7 +249,7 @@ void drain() { R w; try { - w = ObjectHelper.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); + w = Objects.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -261,7 +260,7 @@ void drain() { e++; } else { - ExceptionHelper.addThrowable(error, new MissingBackpressureException("Could not emit value due to lack of requests")); + ExceptionHelper.addThrowable(error, MissingBackpressureException.createDefault()); q.clear(); cancelAll(); errorAll(a); @@ -284,7 +283,7 @@ else if (mode == RIGHT_VALUE) { Publisher p; try { - p = ObjectHelper.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null Publisher"); + p = Objects.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null Publisher"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -311,7 +310,7 @@ else if (mode == RIGHT_VALUE) { R w; try { - w = ObjectHelper.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); + w = Objects.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -322,7 +321,7 @@ else if (mode == RIGHT_VALUE) { e++; } else { - ExceptionHelper.addThrowable(error, new MissingBackpressureException("Could not emit value due to lack of requests")); + ExceptionHelper.addThrowable(error, MissingBackpressureException.createDefault()); q.clear(); cancelAll(); errorAll(a); @@ -340,7 +339,7 @@ else if (mode == LEFT_CLOSE) { lefts.remove(end.index); disposables.remove(end); } - else if (mode == RIGHT_CLOSE) { + else { LeftRightEndSubscriber end = (LeftRightEndSubscriber)val; rights.remove(end.index); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJust.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJust.java index d498befefc..2926d49509 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJust.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJust.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,8 +16,8 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; import io.reactivex.rxjava3.internal.subscriptions.ScalarSubscription; +import io.reactivex.rxjava3.operators.ScalarSupplier; /** * Represents a constant scalar value. @@ -31,7 +31,7 @@ public FlowableJust(final T value) { @Override protected void subscribeActual(Subscriber s) { - s.onSubscribe(new ScalarSubscription(s, value)); + s.onSubscribe(new ScalarSubscription<>(s, value)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastMaybe.java index 1c6a46f71c..056dfe9201 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public FlowableLastMaybe(Publisher source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new LastSubscriber(observer)); + source.subscribe(new LastSubscriber<>(observer)); } static final class LastSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastSingle.java index 7694067609..ef4fb1a015 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ public FlowableLastSingle(Publisher source, T defaultItem) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new LastSubscriber(observer, defaultItem)); + source.subscribe(new LastSubscriber<>(observer, defaultItem)); } static final class LastSubscriber implements FlowableSubscriber, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLift.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLift.java index 5f92e95652..99e4075bc4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLift.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLift.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java index cec093d278..90ad6c2883 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscribers.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; + +import java.util.Objects; public final class FlowableMap extends AbstractFlowableWithUpstream { final Function mapper; @@ -60,7 +61,7 @@ public void onNext(T t) { U v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); + v = Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; @@ -77,7 +78,7 @@ public int requestFusion(int mode) { @Override public U poll() throws Throwable { T t = qs.poll(); - return t != null ? ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; + return t != null ? Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; } } @@ -103,7 +104,7 @@ public void onNext(T t) { U v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); + v = Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; @@ -114,13 +115,18 @@ public void onNext(T t) { @Override public boolean tryOnNext(T t) { if (done) { - return false; + return true; + } + + if (sourceMode != NONE) { + downstream.tryOnNext(null); + return true; } U v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); + v = Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return true; @@ -137,7 +143,7 @@ public int requestFusion(int mode) { @Override public U poll() throws Throwable { T t = qs.poll(); - return t != null ? ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; + return t != null ? Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotification.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotification.java index 9998bee06b..be2d78062e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotification.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotification.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscribers.SinglePostCompleteSubscriber; +import java.util.Objects; + public final class FlowableMapNotification extends AbstractFlowableWithUpstream { final Function onNextMapper; @@ -40,7 +41,7 @@ public FlowableMapNotification( @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new MapNotificationSubscriber(s, onNextMapper, onErrorMapper, onCompleteSupplier)); + source.subscribe(new MapNotificationSubscriber<>(s, onNextMapper, onErrorMapper, onCompleteSupplier)); } static final class MapNotificationSubscriber @@ -66,7 +67,7 @@ public void onNext(T t) { R p; try { - p = ObjectHelper.requireNonNull(onNextMapper.apply(t), "The onNext publisher returned is null"); + p = Objects.requireNonNull(onNextMapper.apply(t), "The onNext publisher returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); @@ -82,7 +83,7 @@ public void onError(Throwable t) { R p; try { - p = ObjectHelper.requireNonNull(onErrorMapper.apply(t), "The onError publisher returned is null"); + p = Objects.requireNonNull(onErrorMapper.apply(t), "The onError publisher returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(new CompositeException(t, e)); @@ -97,7 +98,7 @@ public void onComplete() { R p; try { - p = ObjectHelper.requireNonNull(onCompleteSupplier.get(), "The onComplete publisher returned is null"); + p = Objects.requireNonNull(onCompleteSupplier.get(), "The onComplete publisher returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapPublisher.java index 159efcea6c..cc8ccb8fe8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterialize.java index e3e23c18e5..7a806b8d5d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public FlowableMaterialize(Flowable source) { @Override protected void subscribeActual(Subscriber> s) { - source.subscribe(new MaterializeSubscriber(s)); + source.subscribe(new MaterializeSubscriber<>(s)); } static final class MaterializeSubscriber extends SinglePostCompleteSubscriber> { @@ -46,12 +46,12 @@ public void onNext(T t) { @Override public void onError(Throwable t) { - complete(Notification.createOnError(t)); + complete(Notification.createOnError(t)); } @Override public void onComplete() { - complete(Notification.createOnComplete()); + complete(Notification.createOnComplete()); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletable.java index efb978083c..ccd48a78fa 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public FlowableMergeWithCompletable(Flowable source, CompletableSource other) @Override protected void subscribeActual(Subscriber subscriber) { - MergeWithSubscriber parent = new MergeWithSubscriber(subscriber); + MergeWithSubscriber parent = new MergeWithSubscriber<>(subscriber); subscriber.onSubscribe(parent); source.subscribe(parent); other.subscribe(parent.otherObserver); @@ -68,7 +68,7 @@ static final class MergeWithSubscriber extends AtomicInteger MergeWithSubscriber(Subscriber downstream) { this.downstream = downstream; - this.mainSubscription = new AtomicReference(); + this.mainSubscription = new AtomicReference<>(); this.otherObserver = new OtherObserver(this); this.errors = new AtomicThrowable(); this.requested = new AtomicLong(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybe.java index 4ad7f92d07..e7581bcaed 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,10 +20,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; /** * Merges an Observable and a Maybe by emitting the items of the Observable and the success @@ -43,7 +43,7 @@ public FlowableMergeWithMaybe(Flowable source, MaybeSource other @Override protected void subscribeActual(Subscriber subscriber) { - MergeWithObserver parent = new MergeWithObserver(subscriber); + MergeWithObserver parent = new MergeWithObserver<>(subscriber); subscriber.onSubscribe(parent); source.subscribe(parent); other.subscribe(parent.otherObserver); @@ -88,8 +88,8 @@ static final class MergeWithObserver extends AtomicInteger MergeWithObserver(Subscriber downstream) { this.downstream = downstream; - this.mainSubscription = new AtomicReference(); - this.otherObserver = new OtherObserver(this); + this.mainSubscription = new AtomicReference<>(); + this.otherObserver = new OtherObserver<>(this); this.errors = new AtomicThrowable(); this.requested = new AtomicLong(); this.prefetch = bufferSize(); @@ -211,7 +211,7 @@ void otherComplete() { SimplePlainQueue getOrCreateQueue() { SimplePlainQueue q = queue; if (q == null) { - q = new SpscArrayQueue(bufferSize()); + q = new SpscArrayQueue<>(bufferSize()); queue = q; } return q; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingle.java index 33fe6b8454..b0f3e8b3ed 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,10 +20,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; /** * Merges an Observable and a Maybe by emitting the items of the Observable and the success @@ -43,7 +43,7 @@ public FlowableMergeWithSingle(Flowable source, SingleSource oth @Override protected void subscribeActual(Subscriber subscriber) { - MergeWithObserver parent = new MergeWithObserver(subscriber); + MergeWithObserver parent = new MergeWithObserver<>(subscriber); subscriber.onSubscribe(parent); source.subscribe(parent); other.subscribe(parent.otherObserver); @@ -88,8 +88,8 @@ static final class MergeWithObserver extends AtomicInteger MergeWithObserver(Subscriber downstream) { this.downstream = downstream; - this.mainSubscription = new AtomicReference(); - this.otherObserver = new OtherObserver(this); + this.mainSubscription = new AtomicReference<>(); + this.otherObserver = new OtherObserver<>(this); this.errors = new AtomicThrowable(); this.requested = new AtomicLong(); this.prefetch = bufferSize(); @@ -206,7 +206,7 @@ void otherError(Throwable ex) { SimplePlainQueue getOrCreateQueue() { SimplePlainQueue q = queue; if (q == null) { - q = new SpscArrayQueue(bufferSize()); + q = new SpscArrayQueue<>(bufferSize()); queue = q; } return q; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableNever.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableNever.java index 9dc0611920..83aeb1533d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableNever.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableNever.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.Subscriber; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java index 66605c9520..576debbf2b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,10 +21,12 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableObserveOn extends AbstractFlowableWithUpstream { @@ -50,10 +52,10 @@ public void subscribeActual(Subscriber s) { Worker worker = scheduler.createWorker(); if (s instanceof ConditionalSubscriber) { - source.subscribe(new ObserveOnConditionalSubscriber( + source.subscribe(new ObserveOnConditionalSubscriber<>( (ConditionalSubscriber) s, worker, delayError, prefetch)); } else { - source.subscribe(new ObserveOnSubscriber(s, worker, delayError, prefetch)); + source.subscribe(new ObserveOnSubscriber<>(s, worker, delayError, prefetch)); } } @@ -111,7 +113,7 @@ public final void onNext(T t) { if (!queue.offer(t)) { upstream.cancel(); - error = new MissingBackpressureException("Queue is full?!"); + error = new QueueOverflowException(); done = true; } trySchedule(); @@ -242,8 +244,7 @@ public final boolean isEmpty() { } } - static final class ObserveOnSubscriber extends BaseObserveOnSubscriber - implements FlowableSubscriber { + static final class ObserveOnSubscriber extends BaseObserveOnSubscriber { private static final long serialVersionUID = -4547113800637756442L; @@ -289,7 +290,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); downstream.onSubscribe(this); @@ -350,15 +351,10 @@ void runSync() { return; } - int w = get(); - if (missed == w) { - produced = e; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + produced = e; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -533,7 +529,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); downstream.onSubscribe(this); @@ -593,15 +589,10 @@ void runSync() { return; } - int w = get(); - if (missed == w) { - produced = e; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + produced = e; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -662,16 +653,11 @@ void runAsync() { return; } - int w = get(); - if (missed == w) { - produced = emitted; - consumed = polled; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + produced = emitted; + consumed = polled; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBuffer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBuffer.java index df04ac54db..db58b68a10 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBuffer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBuffer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,30 +20,31 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.Action; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.*; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.*; public final class FlowableOnBackpressureBuffer extends AbstractFlowableWithUpstream { final int bufferSize; final boolean unbounded; final boolean delayError; final Action onOverflow; + final Consumer onDropped; public FlowableOnBackpressureBuffer(Flowable source, int bufferSize, boolean unbounded, - boolean delayError, Action onOverflow) { + boolean delayError, Action onOverflow, Consumer onDropped) { super(source); this.bufferSize = bufferSize; this.unbounded = unbounded; this.delayError = delayError; this.onOverflow = onOverflow; + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new BackpressureBufferSubscriber(s, bufferSize, unbounded, delayError, onOverflow)); + source.subscribe(new BackpressureBufferSubscriber<>(s, bufferSize, unbounded, delayError, onOverflow, onDropped)); } static final class BackpressureBufferSubscriber extends BasicIntQueueSubscription implements FlowableSubscriber { @@ -54,6 +55,7 @@ static final class BackpressureBufferSubscriber extends BasicIntQueueSubscrip final SimplePlainQueue queue; final boolean delayError; final Action onOverflow; + final Consumer onDropped; Subscription upstream; @@ -67,17 +69,18 @@ static final class BackpressureBufferSubscriber extends BasicIntQueueSubscrip boolean outputFused; BackpressureBufferSubscriber(Subscriber actual, int bufferSize, - boolean unbounded, boolean delayError, Action onOverflow) { + boolean unbounded, boolean delayError, Action onOverflow, Consumer onDropped) { this.downstream = actual; this.onOverflow = onOverflow; this.delayError = delayError; + this.onDropped = onDropped; SimplePlainQueue q; if (unbounded) { - q = new SpscLinkedArrayQueue(bufferSize); + q = new SpscLinkedArrayQueue<>(bufferSize); } else { - q = new SpscArrayQueue(bufferSize); + q = new SpscArrayQueue<>(bufferSize); } this.queue = q; @@ -99,6 +102,7 @@ public void onNext(T t) { MissingBackpressureException ex = new MissingBackpressureException("Buffer is full"); try { onOverflow.run(); + onDropped.accept(t); } catch (Throwable e) { Exceptions.throwIfFatal(e); ex.initCause(e); @@ -255,7 +259,7 @@ public int requestFusion(int mode) { @Nullable @Override - public T poll() throws Exception { + public T poll() { return queue.poll(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategy.java index 70b65ab39a..7963fb7d40 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategy.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,7 +20,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -38,17 +38,21 @@ public final class FlowableOnBackpressureBufferStrategy extends AbstractFlowa final BackpressureOverflowStrategy strategy; + final Consumer onDropped; + public FlowableOnBackpressureBufferStrategy(Flowable source, - long bufferSize, Action onOverflow, BackpressureOverflowStrategy strategy) { + long bufferSize, Action onOverflow, BackpressureOverflowStrategy strategy, + Consumer onDropped) { super(source); this.bufferSize = bufferSize; this.onOverflow = onOverflow; this.strategy = strategy; + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new OnBackpressureBufferStrategySubscriber(s, onOverflow, strategy, bufferSize)); + source.subscribe(new OnBackpressureBufferStrategySubscriber<>(s, onOverflow, strategy, bufferSize, onDropped)); } static final class OnBackpressureBufferStrategySubscriber @@ -61,6 +65,8 @@ static final class OnBackpressureBufferStrategySubscriber final Action onOverflow; + final Consumer onDropped; + final BackpressureOverflowStrategy strategy; final long bufferSize; @@ -77,13 +83,15 @@ static final class OnBackpressureBufferStrategySubscriber Throwable error; OnBackpressureBufferStrategySubscriber(Subscriber actual, Action onOverflow, - BackpressureOverflowStrategy strategy, long bufferSize) { + BackpressureOverflowStrategy strategy, long bufferSize, + Consumer onDropped) { this.downstream = actual; this.onOverflow = onOverflow; this.strategy = strategy; this.bufferSize = bufferSize; this.requested = new AtomicLong(); - this.deque = new ArrayDeque(); + this.deque = new ArrayDeque<>(); + this.onDropped = onDropped; } @Override @@ -104,44 +112,60 @@ public void onNext(T t) { } boolean callOnOverflow = false; boolean callError = false; + boolean callDrain = false; Deque dq = deque; + T toDrop = null; synchronized (dq) { if (dq.size() == bufferSize) { switch (strategy) { case DROP_LATEST: - dq.pollLast(); + toDrop = dq.pollLast(); dq.offer(t); callOnOverflow = true; break; case DROP_OLDEST: - dq.poll(); + toDrop = dq.poll(); dq.offer(t); callOnOverflow = true; break; default: // signal error + toDrop = t; callError = true; break; } } else { dq.offer(t); + callDrain = true; } } - if (callOnOverflow) { - if (onOverflow != null) { - try { - onOverflow.run(); - } catch (Throwable ex) { - Exceptions.throwIfFatal(ex); - upstream.cancel(); - onError(ex); - } + if (callOnOverflow && onOverflow != null) { + try { + onOverflow.run(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + onError(ex); } - } else if (callError) { + } + + if (onDropped != null && toDrop != null) { + try { + onDropped.accept(toDrop); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + onError(ex); + } + } + + if (callError) { upstream.cancel(); - onError(new MissingBackpressureException()); - } else { + onError(MissingBackpressureException.createDefault()); + } + + if (callDrain) { drain(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDrop.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDrop.java index 8f3f2d4946..799c5dbca1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDrop.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDrop.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ public void accept(T t) { @Override protected void subscribeActual(Subscriber s) { - this.source.subscribe(new BackpressureDropSubscriber(s, onDrop)); + this.source.subscribe(new BackpressureDropSubscriber<>(s, onDrop)); } static final class BackpressureDropSubscriber diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java index 10304a4b84..acaf165069 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureError.java @@ -1,11 +1,11 @@ -/** +/* * 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. @@ -31,7 +31,7 @@ public FlowableOnBackpressureError(Flowable source) { @Override protected void subscribeActual(Subscriber s) { - this.source.subscribe(new BackpressureErrorSubscriber(s)); + this.source.subscribe(new BackpressureErrorSubscriber<>(s)); } static final class BackpressureErrorSubscriber @@ -65,7 +65,8 @@ public void onNext(T t) { downstream.onNext(t); BackpressureHelper.produced(this, 1); } else { - onError(new MissingBackpressureException("could not emit value due to lack of requests")); + upstream.cancel(); + onError(MissingBackpressureException.createDefault()); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java index b7ba9116a2..155e284e93 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,159 +13,50 @@ package io.reactivex.rxjava3.internal.operators.flowable; -import java.util.concurrent.atomic.*; - -import org.reactivestreams.*; - -import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; -import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; +import org.reactivestreams.Subscriber; public final class FlowableOnBackpressureLatest extends AbstractFlowableWithUpstream { - public FlowableOnBackpressureLatest(Flowable source) { + final Consumer onDropped; + + public FlowableOnBackpressureLatest(Flowable source, Consumer onDropped) { super(source); + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new BackpressureLatestSubscriber(s)); + source.subscribe(new BackpressureLatestSubscriber<>(s, onDropped)); } - static final class BackpressureLatestSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { + static final class BackpressureLatestSubscriber extends AbstractBackpressureThrottlingSubscriber { private static final long serialVersionUID = 163080509307634843L; - final Subscriber downstream; - - Subscription upstream; - - volatile boolean done; - Throwable error; - - volatile boolean cancelled; - - final AtomicLong requested = new AtomicLong(); + final Consumer onDropped; - final AtomicReference current = new AtomicReference(); - - BackpressureLatestSubscriber(Subscriber downstream) { - this.downstream = downstream; - } - - @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(this.upstream, s)) { - this.upstream = s; - downstream.onSubscribe(this); - s.request(Long.MAX_VALUE); - } + BackpressureLatestSubscriber(Subscriber downstream, + Consumer onDropped) { + super(downstream); + this.onDropped = onDropped; } @Override public void onNext(T t) { - current.lazySet(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) { - if (SubscriptionHelper.validate(n)) { - BackpressureHelper.add(requested, n); - drain(); - } - } - - @Override - public void cancel() { - if (!cancelled) { - cancelled = true; - upstream.cancel(); - - if (getAndIncrement() == 0) { - current.lazySet(null); + T oldValue = current.getAndSet(t); + if (onDropped != null && oldValue != null) { + try { + onDropped.accept(oldValue); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + downstream.onError(ex); } } - } - - void drain() { - if (getAndIncrement() != 0) { - return; - } - final Subscriber a = downstream; - int missed = 1; - final AtomicLong r = requested; - final AtomicReference q = current; - - for (;;) { - long e = 0L; - - while (e != r.get()) { - boolean d = done; - T v = q.getAndSet(null); - boolean empty = v == null; - - if (checkTerminated(d, empty, a, q)) { - return; - } - - if (empty) { - break; - } - - a.onNext(v); - - e++; - } - - if (e == r.get() && checkTerminated(done, q.get() == null, a, q)) { - return; - } - - if (e != 0L) { - BackpressureHelper.produced(r, e); - } - - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } - } - - boolean checkTerminated(boolean d, boolean empty, Subscriber a, AtomicReference q) { - if (cancelled) { - q.lazySet(null); - return true; - } - - if (d) { - Throwable e = error; - if (e != null) { - q.lazySet(null); - a.onError(e); - return true; - } else - if (empty) { - a.onComplete(); - return true; - } - } - - return false; + drain(); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java new file mode 100644 index 0000000000..9a6abab89c --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduce.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.BiFunction; +import org.reactivestreams.Subscriber; + +import java.util.Objects; + +public final class FlowableOnBackpressureReduce extends AbstractFlowableWithUpstream { + + final BiFunction reducer; + + public FlowableOnBackpressureReduce(@NonNull Flowable source, @NonNull BiFunction reducer) { + super(source); + this.reducer = reducer; + } + + @Override + protected void subscribeActual(@NonNull Subscriber s) { + source.subscribe(new BackpressureReduceSubscriber<>(s, reducer)); + } + + static final class BackpressureReduceSubscriber extends AbstractBackpressureThrottlingSubscriber { + + private static final long serialVersionUID = 821363947659780367L; + + final BiFunction reducer; + + BackpressureReduceSubscriber(@NonNull Subscriber downstream, @NonNull BiFunction reducer) { + super(downstream); + this.reducer = reducer; + } + + @Override + public void onNext(T t) { + T v = current.get(); + if (v != null) { + v = current.getAndSet(null); + } + if (v == null) { + current.lazySet(t); + } else { + try { + current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + onError(ex); + return; + } + } + drain(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java new file mode 100644 index 0000000000..faed723240 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWith.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.functions.Supplier; +import org.reactivestreams.Subscriber; + +import java.util.Objects; + +public final class FlowableOnBackpressureReduceWith extends AbstractFlowableWithUpstream { + + final BiFunction reducer; + final Supplier supplier; + + public FlowableOnBackpressureReduceWith(@NonNull Flowable source, + @NonNull Supplier supplier, + @NonNull BiFunction reducer) { + super(source); + this.reducer = reducer; + this.supplier = supplier; + } + + @Override + protected void subscribeActual(@NonNull Subscriber s) { + source.subscribe(new BackpressureReduceWithSubscriber<>(s, supplier, reducer)); + } + + static final class BackpressureReduceWithSubscriber extends AbstractBackpressureThrottlingSubscriber { + + private static final long serialVersionUID = 8255923705960622424L; + + final BiFunction reducer; + final Supplier supplier; + + BackpressureReduceWithSubscriber(@NonNull Subscriber downstream, + @NonNull Supplier supplier, + @NonNull BiFunction reducer) { + super(downstream); + this.reducer = reducer; + this.supplier = supplier; + } + + @Override + public void onNext(T t) { + R v = current.get(); + if (v != null) { + v = current.getAndSet(null); + } + try { + if (v == null) { + current.lazySet(Objects.requireNonNull( + reducer.apply(Objects.requireNonNull(supplier.get(), "The supplier returned a null value"), t), + "The reducer returned a null value" + )); + } else { + current.lazySet(Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value")); + } + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + onError(ex); + return; + } + drain(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorComplete.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorComplete.java new file mode 100644 index 0000000000..a0999f25c4 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorComplete.java @@ -0,0 +1,107 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.Predicate; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; + +/** + * Emits an onComplete if the source emits an onError and the predicate returns true for + * that Throwable. + * + * @param the value type + * @since 3.0.0 + */ +public final class FlowableOnErrorComplete extends AbstractFlowableWithUpstream { + + final Predicate predicate; + + public FlowableOnErrorComplete(Flowable source, + Predicate predicate) { + super(source); + this.predicate = predicate; + } + + @Override + protected void subscribeActual(Subscriber observer) { + source.subscribe(new OnErrorCompleteSubscriber<>(observer, predicate)); + } + + public static final class OnErrorCompleteSubscriber + implements FlowableSubscriber, Subscription { + + final Subscriber downstream; + + final Predicate predicate; + + Subscription upstream; + + public OnErrorCompleteSubscriber(Subscriber actual, Predicate predicate) { + this.downstream = actual; + this.predicate = predicate; + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(this.upstream, s)) { + this.upstream = s; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T value) { + downstream.onNext(value); + } + + @Override + public void onError(Throwable e) { + boolean b; + + try { + b = predicate.test(e); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(new CompositeException(e, ex)); + return; + } + + if (b) { + downstream.onComplete(); + } else { + downstream.onError(e); + } + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public void cancel() { + upstream.cancel(); + } + + @Override + public void request(long n) { + upstream.request(n); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorNext.java index 7a186cdec0..cb33b81a2f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionArbiter; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class FlowableOnErrorNext extends AbstractFlowableWithUpstream { final Function> nextSupplier; @@ -33,7 +34,7 @@ public FlowableOnErrorNext(Flowable source, @Override protected void subscribeActual(Subscriber s) { - OnErrorNextSubscriber parent = new OnErrorNextSubscriber(s, nextSupplier); + OnErrorNextSubscriber parent = new OnErrorNextSubscriber<>(s, nextSupplier); s.onSubscribe(parent); source.subscribe(parent); } @@ -90,7 +91,7 @@ public void onError(Throwable t) { Publisher p; try { - p = ObjectHelper.requireNonNull(nextSupplier.apply(t), "The nextSupplier returned a null Publisher"); + p = Objects.requireNonNull(nextSupplier.apply(t), "The nextSupplier returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(new CompositeException(t, e)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturn.java index df57dd5c00..85a375fac9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscribers.SinglePostCompleteSubscriber; +import java.util.Objects; + public final class FlowableOnErrorReturn extends AbstractFlowableWithUpstream { final Function valueSupplier; public FlowableOnErrorReturn(Flowable source, Function valueSupplier) { @@ -30,7 +31,7 @@ public FlowableOnErrorReturn(Flowable source, Function s) { - source.subscribe(new OnErrorReturnSubscriber(s, valueSupplier)); + source.subscribe(new OnErrorReturnSubscriber<>(s, valueSupplier)); } static final class OnErrorReturnSubscriber @@ -54,7 +55,7 @@ public void onNext(T t) { public void onError(Throwable t) { T v; try { - v = ObjectHelper.requireNonNull(valueSupplier.apply(t), "The valueSupplier returned a null value"); + v = Objects.requireNonNull(valueSupplier.apply(t), "The valueSupplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(new CompositeException(t, ex)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java index 452dc83cce..b05bc8b748 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublish.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,9 +23,11 @@ import io.reactivex.rxjava3.flowables.ConnectableFlowable; import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -34,7 +36,7 @@ * completes or the connection is disposed. *

* The difference to FlowablePublish is that when the upstream terminates, - * late subscriberss will receive that terminal event until the connection is + * late subscribers will receive that terminal event until the connection is * disposed and the ConnectableFlowable is reset to its fresh state. * * @param the element type @@ -52,7 +54,7 @@ public final class FlowablePublish extends ConnectableFlowable public FlowablePublish(Publisher source, int bufferSize) { this.source = source; this.bufferSize = bufferSize; - this.current = new AtomicReference>(); + this.current = new AtomicReference<>(); } @Override @@ -60,14 +62,6 @@ public Publisher source() { return source; } - /** - * The internal buffer size of this FloawblePublishAlt operator. - * @return The internal buffer size of this FloawblePublishAlt operator. - */ - public int publishBufferSize() { - return bufferSize; - } - @Override public void connect(Consumer connection) { PublishConnection conn; @@ -77,7 +71,7 @@ public void connect(Consumer connection) { conn = current.get(); if (conn == null || conn.isDisposed()) { - PublishConnection fresh = new PublishConnection(current, bufferSize); + PublishConnection fresh = new PublishConnection<>(current, bufferSize); if (!current.compareAndSet(conn, fresh)) { continue; } @@ -109,7 +103,7 @@ protected void subscribeActual(Subscriber s) { // don't create a fresh connection if the current is disposed if (conn == null) { - PublishConnection fresh = new PublishConnection(current, bufferSize); + PublishConnection fresh = new PublishConnection<>(current, bufferSize); if (!current.compareAndSet(conn, fresh)) { continue; } @@ -119,7 +113,7 @@ protected void subscribeActual(Subscriber s) { break; } - InnerSubscription inner = new InnerSubscription(s, conn); + InnerSubscription inner = new InnerSubscription<>(s, conn); s.onSubscribe(inner); if (conn.add(inner)) { @@ -180,10 +174,10 @@ static final class PublishConnection @SuppressWarnings("unchecked") PublishConnection(AtomicReference> current, int bufferSize) { this.current = current; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.connect = new AtomicBoolean(); this.bufferSize = bufferSize; - this.subscribers = new AtomicReference[]>(EMPTY); + this.subscribers = new AtomicReference<>(EMPTY); } @SuppressWarnings("unchecked") @@ -222,7 +216,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(bufferSize); + queue = new SpscArrayQueue<>(bufferSize); s.request(bufferSize); } @@ -232,7 +226,7 @@ public void onSubscribe(Subscription s) { public void onNext(T t) { // we expect upstream to honor backpressure requests if (sourceMode == QueueSubscription.NONE && !queue.offer(t)) { - onError(new MissingBackpressureException("Prefetch queue is full?!")); + onError(new QueueOverflowException()); return; } // since many things can happen concurrently, we have a common dispatch diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java index cc36e86821..08d0e40058 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticast.java @@ -1,33 +1,30 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -55,19 +52,19 @@ public FlowablePublishMulticast(Flowable source, @Override protected void subscribeActual(Subscriber s) { - MulticastProcessor mp = new MulticastProcessor(prefetch, delayError); + MulticastProcessor mp = new MulticastProcessor<>(prefetch, delayError); Publisher other; try { - other = ObjectHelper.requireNonNull(selector.apply(mp), "selector returned a null Publisher"); + other = Objects.requireNonNull(selector.apply(mp), "selector returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptySubscription.error(ex, s); return; } - OutputCanceller out = new OutputCanceller(s, mp); + OutputCanceller out = new OutputCanceller<>(s, mp); other.subscribe(out); @@ -124,7 +121,7 @@ public void cancel() { } } - static final class MulticastProcessor extends Flowable implements FlowableSubscriber, Disposable { + static final class MulticastProcessor extends Flowable implements FlowableSubscriber { @SuppressWarnings("rawtypes") static final MulticastSubscription[] EMPTY = new MulticastSubscription[0]; @@ -159,8 +156,8 @@ static final class MulticastProcessor extends Flowable implements Flowable this.limit = prefetch - (prefetch >> 2); // request after 75% consumption this.delayError = delayError; this.wip = new AtomicInteger(); - this.upstream = new AtomicReference(); - this.subscribers = new AtomicReference[]>(EMPTY); + this.upstream = new AtomicReference<>(); + this.subscribers = new AtomicReference<>(EMPTY); } @Override @@ -192,19 +189,19 @@ public void onSubscribe(Subscription s) { } } - @Override - public void dispose() { - SubscriptionHelper.cancel(upstream); - if (wip.getAndIncrement() == 0) { - SimpleQueue q = queue; - if (q != null) { - q.clear(); + void dispose() { + if (!done) { + SubscriptionHelper.cancel(upstream); + if (wip.getAndIncrement() == 0) { + SimpleQueue q = queue; + if (q != null) { + q.clear(); + } } } } - @Override - public boolean isDisposed() { + boolean isDisposed() { return upstream.get() == SubscriptionHelper.CANCELLED; } @@ -215,7 +212,7 @@ public void onNext(T t) { } if (sourceMode == QueueSubscription.NONE && !queue.offer(t)) { upstream.get().cancel(); - onError(new MissingBackpressureException()); + onError(MissingBackpressureException.createDefault()); return; } drain(); @@ -293,7 +290,7 @@ void remove(MulticastSubscription s) { @Override protected void subscribeActual(Subscriber s) { - MulticastSubscription ms = new MulticastSubscription(s, this); + MulticastSubscription ms = new MulticastSubscription<>(s, this); s.onSubscribe(ms); if (add(ms)) { if (ms.isCancelled()) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRange.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRange.java index 9c14349eb9..e2e3b9a6fb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRange.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRange.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,9 +17,9 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; /** * Emits a range of integer values. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLong.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLong.java index 8ae2f333ae..3be56da6ef 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLong.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLong.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,9 +17,9 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; /** * Emits a range of long values. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduce.java index 4da1d68d99..99bc861519 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduce.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduce.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduces a sequence via a function into a single value or signals NoSuchElementException for * an empty source. @@ -39,7 +40,7 @@ public FlowableReduce(Flowable source, BiFunction reducer) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ReduceSubscriber(s, reducer)); + source.subscribe(new ReduceSubscriber<>(s, reducer)); } static final class ReduceSubscriber extends DeferredScalarSubscription implements FlowableSubscriber { @@ -77,7 +78,7 @@ public void onNext(T t) { value = t; } else { try { - value = ObjectHelper.requireNonNull(reducer.apply(v, t), "The reducer returned a null value"); + value = Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceMaybe.java index 72b4f1ac28..6541fee6bb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,11 +19,12 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduce a Flowable into a single value exposed as Single or signal NoSuchElementException. * @@ -49,12 +50,12 @@ public Publisher source() { @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableReduce(source, reducer)); + return RxJavaPlugins.onAssembly(new FlowableReduce<>(source, reducer)); } @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new ReduceSubscriber(observer, reducer)); + source.subscribe(new ReduceSubscriber<>(observer, reducer)); } static final class ReduceSubscriber implements FlowableSubscriber, Disposable { @@ -105,7 +106,7 @@ public void onNext(T t) { value = t; } else { try { - value = ObjectHelper.requireNonNull(reducer.apply(v, t), "The reducer returned a null value"); + value = Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceSeedSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceSeedSingle.java index cf489963ed..a5a8cc40cb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceSeedSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceSeedSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,11 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduce a sequence of values, starting from a seed value and by using * an accumulator function and return the last accumulated value. @@ -46,7 +47,7 @@ public FlowableReduceSeedSingle(Publisher source, R seed, BiFunction observer) { - source.subscribe(new ReduceSeedObserver(observer, reducer, seed)); + source.subscribe(new ReduceSeedObserver<>(observer, reducer, seed)); } static final class ReduceSeedObserver implements FlowableSubscriber, Disposable { @@ -81,7 +82,7 @@ public void onNext(T value) { R v = this.value; if (v != null) { try { - this.value = ObjectHelper.requireNonNull(reducer.apply(v, value), "The reducer returned a null value"); + this.value = Objects.requireNonNull(reducer.apply(v, value), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingle.java index ca8be6779e..9d27a19668 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,9 +19,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.flowable.FlowableReduceSeedSingle.ReduceSeedObserver; +import java.util.Objects; + /** * Reduce a sequence of values, starting from a generated seed value and by using * an accumulator function and return the last accumulated value. @@ -48,12 +49,12 @@ protected void subscribeActual(SingleObserver observer) { R seed; try { - seed = ObjectHelper.requireNonNull(seedSupplier.get(), "The seedSupplier returned a null value"); + seed = Objects.requireNonNull(seedSupplier.get(), "The seedSupplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); return; } - source.subscribe(new ReduceSeedObserver(observer, reducer, seed)); + source.subscribe(new ReduceSeedObserver<>(observer, reducer, seed)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount.java index e8f7f895ba..0c9f17b0bd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -84,7 +84,7 @@ protected void subscribeActual(Subscriber s) { } } - source.subscribe(new RefCountSubscriber(s, this, conn)); + source.subscribe(new RefCountSubscriber<>(s, this, conn)); if (connect) { source.connect(conn); @@ -168,7 +168,7 @@ public void run() { } @Override - public void accept(Disposable t) throws Exception { + public void accept(Disposable t) { DisposableHelper.replace(this, t); synchronized (parent) { if (disconnectedEarly) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeat.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeat.java index 092703b343..ffc90b8f4b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeat.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeat.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public void subscribeActual(Subscriber s) { SubscriptionArbiter sa = new SubscriptionArbiter(false); s.onSubscribe(sa); - RepeatSubscriber rs = new RepeatSubscriber(s, count != Long.MAX_VALUE ? count - 1 : Long.MAX_VALUE, sa, source); + RepeatSubscriber rs = new RepeatSubscriber<>(s, count != Long.MAX_VALUE ? count - 1 : Long.MAX_VALUE, sa, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatUntil.java index 9e24575c58..1d527fa59c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public void subscribeActual(Subscriber s) { SubscriptionArbiter sa = new SubscriptionArbiter(false); s.onSubscribe(sa); - RepeatSubscriber rs = new RepeatSubscriber(s, until, sa, source); + RepeatSubscriber rs = new RepeatSubscriber<>(s, until, sa, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatWhen.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatWhen.java index 021b4dd387..a2bfa250e5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatWhen.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subscribers.SerializedSubscriber; @@ -37,23 +37,23 @@ public FlowableRepeatWhen(Flowable source, @Override public void subscribeActual(Subscriber s) { - SerializedSubscriber z = new SerializedSubscriber(s); + SerializedSubscriber z = new SerializedSubscriber<>(s); - FlowableProcessor processor = UnicastProcessor.create(8).toSerialized(); + FlowableProcessor processor = UnicastProcessor.create(8).toSerialized(); Publisher when; try { - when = ObjectHelper.requireNonNull(handler.apply(processor), "handler returned a null Publisher"); + when = Objects.requireNonNull(handler.apply(processor), "handler returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptySubscription.error(ex, s); return; } - WhenReceiver receiver = new WhenReceiver(source); + WhenReceiver receiver = new WhenReceiver<>(source); - RepeatWhenSubscriber subscriber = new RepeatWhenSubscriber(z, processor, receiver); + RepeatWhenSubscriber subscriber = new RepeatWhenSubscriber<>(z, processor, receiver); receiver.subscriber = subscriber; @@ -80,7 +80,7 @@ static final class WhenReceiver WhenReceiver(Publisher source) { this.source = source; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.requested = new AtomicLong(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java index 8d9e2e8460..11489490c7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -56,7 +56,7 @@ public final class FlowableReplay extends ConnectableFlowable implements H public static Flowable multicastSelector( final Supplier> connectableFactory, final Function, ? extends Publisher> selector) { - return new MulticastFlowable(connectableFactory, selector); + return new MulticastFlowable<>(connectableFactory, selector); } /** @@ -83,7 +83,7 @@ public static ConnectableFlowable create(Flowable source, if (bufferSize == Integer.MAX_VALUE) { return createFrom(source); } - return create(source, new ReplayBufferSupplier(bufferSize, eagerTruncate)); + return create(source, new ReplayBufferSupplier<>(bufferSize, eagerTruncate)); } /** @@ -114,21 +114,22 @@ public static ConnectableFlowable create(Flowable source, */ public static ConnectableFlowable create(Flowable source, final long maxAge, final TimeUnit unit, final Scheduler scheduler, final int bufferSize, boolean eagerTruncate) { - return create(source, new ScheduledReplayBufferSupplier(bufferSize, maxAge, unit, scheduler, eagerTruncate)); + return create(source, new ScheduledReplayBufferSupplier<>(bufferSize, maxAge, unit, scheduler, eagerTruncate)); } /** - * Creates a OperatorReplay instance to replay values of the given source observable. - * @param source the source observable - * @param bufferFactory the factory to instantiate the appropriate buffer when the observable becomes active - * @return the connectable observable + * Creates a OperatorReplay instance to replay values of the given source {@code Flowable}. + * @param the value type + * @param source the source {@code Flowable} to use + * @param bufferFactory the factory to instantiate the appropriate buffer when the {@code Flowable} becomes active + * @return the {@code ConnectableFlowable} instance */ static ConnectableFlowable create(Flowable source, final Supplier> bufferFactory) { // the current connection to source needs to be shared between the operator and its onSubscribe call - final AtomicReference> curr = new AtomicReference>(); - Publisher onSubscribe = new ReplayPublisher(curr, bufferFactory); - return RxJavaPlugins.onAssembly(new FlowableReplay(onSubscribe, source, curr, bufferFactory)); + final AtomicReference> curr = new AtomicReference<>(); + Publisher onSubscribe = new ReplayPublisher<>(curr, bufferFactory); + return RxJavaPlugins.onAssembly(new FlowableReplay<>(onSubscribe, source, curr, bufferFactory)); } private FlowableReplay(Publisher onSubscribe, Flowable source, @@ -179,7 +180,7 @@ public void connect(Consumer connection) { } // create a new subscriber-to-source - ReplaySubscriber u = new ReplaySubscriber(buf); + ReplaySubscriber u = new ReplaySubscriber<>(buf, current); // try setting it as the current subscriber-to-source if (!current.compareAndSet(ps, u)) { // did not work, perhaps a new subscriber arrived @@ -209,6 +210,7 @@ public void connect(Consumer connection) { try { connection.accept(ps); } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); if (doConnect) { ps.shouldConnect.compareAndSet(true, false); } @@ -245,16 +247,18 @@ static final class ReplaySubscriber final AtomicInteger management; - /** Contains the maximum element index the child Subscribers requested so far. Accessed while emitting is true. */ - long maxChildRequested; - /** Counts the outstanding upstream requests until the producer arrives. */ - long maxUpstreamRequested; + /** Tracks the amount already requested from the upstream. */ + long requestedFromUpstream; + + /** The current connection. */ + final AtomicReference> current; @SuppressWarnings("unchecked") - ReplaySubscriber(ReplayBuffer buffer) { + ReplaySubscriber(ReplayBuffer buffer, AtomicReference> current) { this.buffer = buffer; + this.current = current; this.management = new AtomicInteger(); - this.subscribers = new AtomicReference[]>(EMPTY); + this.subscribers = new AtomicReference<>(EMPTY); this.shouldConnect = new AtomicBoolean(); } @@ -267,9 +271,7 @@ public boolean isDisposed() { @Override public void dispose() { subscribers.set(TERMINATED); - // unlike OperatorPublish, we can't null out the terminated so - // late subscribers can still get replay - // current.compareAndSet(ReplaySubscriber.this, null); + current.compareAndSet(ReplaySubscriber.this, null); // we don't care if it fails because it means the current has // been replaced in the meantime SubscriptionHelper.cancel(this); @@ -283,9 +285,6 @@ public void dispose() { */ @SuppressWarnings("unchecked") boolean add(InnerSubscription producer) { - if (producer == null) { - throw new NullPointerException(); - } // the state can change so we do a CAS loop to achieve atomicity for (;;) { // get the current producer array @@ -414,7 +413,8 @@ public void onComplete() { * Coordinates the request amounts of various child Subscribers. */ void manageRequests() { - if (management.getAndIncrement() != 0) { + AtomicInteger m = management; + if (m.getAndIncrement() != 0) { return; } int missed = 1; @@ -423,46 +423,29 @@ void manageRequests() { if (isDisposed()) { return; } + Subscription p = get(); - InnerSubscription[] a = subscribers.get(); - - long ri = maxChildRequested; - long maxTotalRequests = ri; + // only request when there is an upstream Subscription available + if (p != null) { + // how many items were requested so far + long alreadyRequested = requestedFromUpstream; + long downstreamMaxRequest = alreadyRequested; - for (InnerSubscription rp : a) { - maxTotalRequests = Math.max(maxTotalRequests, rp.totalRequested.get()); - } - - long ur = maxUpstreamRequested; - Subscription p = get(); + // find out the maximum total requested of the current subscribers + for (InnerSubscription rp : subscribers.get()) { + downstreamMaxRequest = Math.max(downstreamMaxRequest, rp.totalRequested.get()); + } - long diff = maxTotalRequests - ri; - if (diff != 0L) { - maxChildRequested = maxTotalRequests; - if (p != null) { - if (ur != 0L) { - maxUpstreamRequested = 0L; - p.request(ur + diff); - } else { - p.request(diff); - } - } else { - // collect upstream request amounts until there is a producer for them - long u = ur + diff; - if (u < 0) { - u = Long.MAX_VALUE; - } - maxUpstreamRequested = u; + // how much more to request from the upstream + long diff = downstreamMaxRequest - alreadyRequested; + if (diff != 0L) { + // save the new maximum requested + requestedFromUpstream = downstreamMaxRequest; + p.request(diff); } - } else - // if there were outstanding upstream requests and we have a producer - if (ur != 0L && p != null) { - maxUpstreamRequested = 0L; - // fire the accumulated requests - p.request(ur); } - missed = management.addAndGet(-missed); + missed = m.addAndGet(-missed); if (missed == 0) { break; } @@ -562,6 +545,7 @@ public void dispose() { } /** * Convenience method to auto-cast the index object. + * @param type to cast index object * @return the current index object */ @SuppressWarnings("unchecked") @@ -667,6 +651,8 @@ public void replay(InnerSubscription output) { output.dispose(); if (!NotificationLite.isError(o) && !NotificationLite.isComplete(o)) { child.onError(err); + } else { + RxJavaPlugins.onError(err); } return; } @@ -716,7 +702,7 @@ static final class Node extends AtomicReference { * * @param the value type */ - static class BoundedReplayBuffer extends AtomicReference implements ReplayBuffer { + abstract static class BoundedReplayBuffer extends AtomicReference implements ReplayBuffer { private static final long serialVersionUID = 2346567790059478686L; @@ -828,11 +814,6 @@ public final void replay(InnerSubscription output) { output.emitting = true; } for (;;) { - if (output.isDisposed()) { - output.index = null; - return; - } - long r = output.get(); boolean unbounded = r == Long.MAX_VALUE; // NOPMD long e = 0L; @@ -846,6 +827,11 @@ public final void replay(InnerSubscription output) { } while (r != 0) { + if (output.isDisposed()) { + output.index = null; + return; + } + Node v = node.get(); if (v != null) { Object o = leaveTransform(v.value); @@ -860,6 +846,8 @@ public final void replay(InnerSubscription output) { output.dispose(); if (!NotificationLite.isError(o) && !NotificationLite.isComplete(o)) { output.child.onError(err); + } else { + RxJavaPlugins.onError(err); } return; } @@ -869,10 +857,11 @@ public final void replay(InnerSubscription output) { } else { break; } - if (output.isDisposed()) { - output.index = null; - return; - } + } + + if (r == 0 && output.isDisposed()) { + output.index = null; + return; } if (e != 0L) { @@ -916,9 +905,7 @@ Object leaveTransform(Object value) { * Override this method to truncate a non-terminated buffer * based on its current properties. */ - void truncate() { - - } + abstract void truncate(); /** * Override this method to truncate a terminated buffer * based on its properties (i.e., truncate but the very last node). @@ -1003,7 +990,7 @@ static final class SizeAndTimeBoundReplayBuffer extends BoundedReplayBuffer(value, terminal ? Long.MAX_VALUE : scheduler.now(unit), unit); + return new Timed<>(value, terminal ? Long.MAX_VALUE : scheduler.now(unit), unit); } @Override @@ -1020,7 +1007,7 @@ void truncate() { int e = 0; for (;;) { - if (next != null && size > 1) { // never truncate the very last item just added + if (size > 1) { // never truncate the very last item just added if (size > limit) { e++; size--; @@ -1055,7 +1042,7 @@ void truncateFinal() { int e = 0; for (;;) { - if (next != null && size > 1) { + if (size > 1) { Timed v = (Timed)next.value; if (v.time() <= timeLimit) { e++; @@ -1127,7 +1114,7 @@ protected void subscribeActual(Subscriber child) { return; } - final SubscriberResourceWrapper srw = new SubscriberResourceWrapper(child); + final SubscriberResourceWrapper srw = new SubscriberResourceWrapper<>(child); observable.subscribe(srw); @@ -1148,31 +1135,6 @@ public void accept(Disposable r) { } } - static final class ConnectableFlowableReplay extends ConnectableFlowable { - private final ConnectableFlowable cf; - private final Flowable flowable; - - ConnectableFlowableReplay(ConnectableFlowable cf, Flowable flowable) { - this.cf = cf; - this.flowable = flowable; - } - - @Override - public void connect(Consumer connection) { - cf.connect(connection); - } - - @Override - public void reset() { - cf.reset(); - } - - @Override - protected void subscribeActual(Subscriber s) { - flowable.subscribe(s); - } - } - static final class ReplayBufferSupplier implements Supplier> { final int bufferSize; @@ -1186,7 +1148,7 @@ static final class ReplayBufferSupplier implements Supplier> @Override public ReplayBuffer get() { - return new SizeBoundReplayBuffer(bufferSize, eagerTruncate); + return new SizeBoundReplayBuffer<>(bufferSize, eagerTruncate); } } @@ -1208,7 +1170,7 @@ static final class ScheduledReplayBufferSupplier implements Supplier get() { - return new SizeAndTimeBoundReplayBuffer(bufferSize, maxAge, unit, scheduler, eagerTruncate); + return new SizeAndTimeBoundReplayBuffer<>(bufferSize, maxAge, unit, scheduler, eagerTruncate); } } @@ -1240,7 +1202,7 @@ public void subscribe(Subscriber child) { return; } // create a new subscriber to source - ReplaySubscriber u = new ReplaySubscriber(buf); + ReplaySubscriber u = new ReplaySubscriber<>(buf, curr); // let's try setting it as the current subscriber-to-source if (!curr.compareAndSet(null, u)) { // didn't work, maybe someone else did it or the current subscriber @@ -1252,7 +1214,7 @@ public void subscribe(Subscriber child) { } // create the backpressure-managing producer for this child - InnerSubscription inner = new InnerSubscription(r, child); + InnerSubscription inner = new InnerSubscription<>(r, child); // the producer has been registered with the current subscriber-to-source so // at least it will receive the next terminal event // setting the producer will trigger the first request to be considered by @@ -1281,7 +1243,7 @@ public void subscribe(Subscriber child) { static final class DefaultUnboundedFactory implements Supplier { @Override public Object get() { - return new UnboundedReplayBuffer(16); + return new UnboundedReplayBuffer<>(16); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryBiPredicate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryBiPredicate.java index 1a06e02bb6..8c8a0e960e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryBiPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryBiPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public void subscribeActual(Subscriber s) { SubscriptionArbiter sa = new SubscriptionArbiter(false); s.onSubscribe(sa); - RetryBiSubscriber rs = new RetryBiSubscriber(s, predicate, sa, source); + RetryBiSubscriber rs = new RetryBiSubscriber<>(s, predicate, sa, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryPredicate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryPredicate.java index 07d7207d74..a954a09300 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public void subscribeActual(Subscriber s) { SubscriptionArbiter sa = new SubscriptionArbiter(false); s.onSubscribe(sa); - RetrySubscriber rs = new RetrySubscriber(s, count, predicate, sa, source); + RetrySubscriber rs = new RetrySubscriber<>(s, count, predicate, sa, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWhen.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWhen.java index ff7ddb84af..9babc65472 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWhen.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,12 +18,13 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.flowable.FlowableRepeatWhen.*; import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subscribers.SerializedSubscriber; +import java.util.Objects; + public final class FlowableRetryWhen extends AbstractFlowableWithUpstream { final Function, ? extends Publisher> handler; @@ -35,23 +36,23 @@ public FlowableRetryWhen(Flowable source, @Override public void subscribeActual(Subscriber s) { - SerializedSubscriber z = new SerializedSubscriber(s); + SerializedSubscriber z = new SerializedSubscriber<>(s); FlowableProcessor processor = UnicastProcessor.create(8).toSerialized(); Publisher when; try { - when = ObjectHelper.requireNonNull(handler.apply(processor), "handler returned a null Publisher"); + when = Objects.requireNonNull(handler.apply(processor), "handler returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptySubscription.error(ex, s); return; } - WhenReceiver receiver = new WhenReceiver(source); + WhenReceiver receiver = new WhenReceiver<>(source); - RetryWhenSubscriber subscriber = new RetryWhenSubscriber(z, processor, receiver); + RetryWhenSubscriber subscriber = new RetryWhenSubscriber<>(z, processor, receiver); receiver.subscriber = subscriber; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSamplePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSamplePublisher.java index 59a6ec3a14..19474d2cc0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSamplePublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSamplePublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,11 +37,11 @@ public FlowableSamplePublisher(Publisher source, Publisher other, boolean @Override protected void subscribeActual(Subscriber s) { - SerializedSubscriber serial = new SerializedSubscriber(s); + SerializedSubscriber serial = new SerializedSubscriber<>(s); if (emitLast) { - source.subscribe(new SampleMainEmitLast(serial, other)); + source.subscribe(new SampleMainEmitLast<>(serial, other)); } else { - source.subscribe(new SampleMainNoLast(serial, other)); + source.subscribe(new SampleMainNoLast<>(serial, other)); } } @@ -54,7 +54,7 @@ abstract static class SamplePublisherSubscriber extends AtomicReference im final AtomicLong requested = new AtomicLong(); - final AtomicReference other = new AtomicReference(); + final AtomicReference other = new AtomicReference<>(); Subscription upstream; @@ -69,7 +69,7 @@ public void onSubscribe(Subscription s) { this.upstream = s; downstream.onSubscribe(this); if (other.get() == null) { - sampler.subscribe(new SamplerSubscriber(this)); + sampler.subscribe(new SamplerSubscriber<>(this)); s.request(Long.MAX_VALUE); } } @@ -129,7 +129,7 @@ void emit() { BackpressureHelper.produced(requested, 1); } else { cancel(); - downstream.onError(new MissingBackpressureException("Couldn't emit value due to lack of requests!")); + downstream.onError(MissingBackpressureException.createDefault()); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTimed.java index ea9a4d5ae2..40551f4a8e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.*; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; @@ -29,24 +31,25 @@ public final class FlowableSampleTimed extends AbstractFlowableWithUpstream onDropped; - public FlowableSampleTimed(Flowable source, long period, TimeUnit unit, Scheduler scheduler, boolean emitLast) { + public FlowableSampleTimed(Flowable source, long period, TimeUnit unit, Scheduler scheduler, boolean emitLast, Consumer onDropped) { super(source); this.period = period; this.unit = unit; this.scheduler = scheduler; this.emitLast = emitLast; + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - SerializedSubscriber serial = new SerializedSubscriber(s); + SerializedSubscriber serial = new SerializedSubscriber<>(s); if (emitLast) { - source.subscribe(new SampleTimedEmitLast(serial, period, unit, scheduler)); + source.subscribe(new SampleTimedEmitLast<>(serial, period, unit, scheduler, onDropped)); } else { - source.subscribe(new SampleTimedNoLast(serial, period, unit, scheduler)); + source.subscribe(new SampleTimedNoLast<>(serial, period, unit, scheduler, onDropped)); } } @@ -58,6 +61,7 @@ abstract static class SampleTimedSubscriber extends AtomicReference implem final long period; final TimeUnit unit; final Scheduler scheduler; + final Consumer onDropped; final AtomicLong requested = new AtomicLong(); @@ -65,11 +69,12 @@ abstract static class SampleTimedSubscriber extends AtomicReference implem Subscription upstream; - SampleTimedSubscriber(Subscriber actual, long period, TimeUnit unit, Scheduler scheduler) { + SampleTimedSubscriber(Subscriber actual, long period, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { this.downstream = actual; this.period = period; this.unit = unit; this.scheduler = scheduler; + this.onDropped = onDropped; } @Override @@ -84,7 +89,17 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { - lazySet(t); + T oldValue = getAndSet(t); + if (oldValue != null && onDropped != null) { + try { + onDropped.accept(oldValue); + } catch (Throwable throwable) { + Exceptions.throwIfFatal(throwable); + cancelTimer(); + upstream.cancel(); + downstream.onError(throwable); + } + } } @Override @@ -125,7 +140,7 @@ void emit() { BackpressureHelper.produced(requested, 1); } else { cancel(); - downstream.onError(new MissingBackpressureException("Couldn't emit value due to lack of requests!")); + downstream.onError(MissingBackpressureException.createDefault()); } } } @@ -137,8 +152,8 @@ static final class SampleTimedNoLast extends SampleTimedSubscriber { private static final long serialVersionUID = -7139995637533111443L; - SampleTimedNoLast(Subscriber actual, long period, TimeUnit unit, Scheduler scheduler) { - super(actual, period, unit, scheduler); + SampleTimedNoLast(Subscriber actual, long period, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { + super(actual, period, unit, scheduler, onDropped); } @Override @@ -158,8 +173,8 @@ static final class SampleTimedEmitLast extends SampleTimedSubscriber { final AtomicInteger wip; - SampleTimedEmitLast(Subscriber actual, long period, TimeUnit unit, Scheduler scheduler) { - super(actual, period, unit, scheduler); + SampleTimedEmitLast(Subscriber actual, long period, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { + super(actual, period, unit, scheduler, onDropped); this.wip = new AtomicInteger(1); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMap.java index 999cf48a51..cb6471b878 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Utility classes to work with scalar-sourced XMap operators (where X == { flat, concat, switch }). */ @@ -64,7 +65,7 @@ public static boolean tryScalarXMapSubscribe(Publisher source, Publisher r; try { - r = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + r = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptySubscription.error(ex, subscriber); @@ -86,7 +87,7 @@ public static boolean tryScalarXMapSubscribe(Publisher source, EmptySubscription.complete(subscriber); return true; } - subscriber.onSubscribe(new ScalarSubscription(subscriber, u)); + subscriber.onSubscribe(new ScalarSubscription<>(subscriber, u)); } else { r.subscribe(subscriber); } @@ -107,7 +108,7 @@ public static boolean tryScalarXMapSubscribe(Publisher source, * @return the new Flowable instance */ public static Flowable scalarXMap(final T value, final Function> mapper) { - return RxJavaPlugins.onAssembly(new ScalarXMapFlowable(value, mapper)); + return RxJavaPlugins.onAssembly(new ScalarXMapFlowable<>(value, mapper)); } /** @@ -133,8 +134,9 @@ static final class ScalarXMapFlowable extends Flowable { public void subscribeActual(Subscriber s) { Publisher other; try { - other = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null Publisher"); + other = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null Publisher"); } catch (Throwable e) { + Exceptions.throwIfFatal(e); EmptySubscription.error(e, s); return; } @@ -153,7 +155,7 @@ public void subscribeActual(Subscriber s) { EmptySubscription.complete(s); return; } - s.onSubscribe(new ScalarSubscription(s, u)); + s.onSubscribe(new ScalarSubscription<>(s, u)); } else { other.subscribe(s); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScan.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScan.java index 3dadb2968b..84c348adfb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScan.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScan.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class FlowableScan extends AbstractFlowableWithUpstream { final BiFunction accumulator; public FlowableScan(Flowable source, BiFunction accumulator) { @@ -31,7 +32,7 @@ public FlowableScan(Flowable source, BiFunction accumulator) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ScanSubscriber(s, accumulator)); + source.subscribe(new ScanSubscriber<>(s, accumulator)); } static final class ScanSubscriber implements FlowableSubscriber, Subscription { @@ -71,7 +72,7 @@ public void onNext(T t) { T u; try { - u = ObjectHelper.requireNonNull(accumulator.apply(v, t), "The value returned by the accumulator is null"); + u = Objects.requireNonNull(accumulator.apply(v, t), "The value returned by the accumulator is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanSeed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanSeed.java index c1875f3070..2ffbc6c8e2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanSeed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanSeed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,10 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -19,11 +21,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableScanSeed extends AbstractFlowableWithUpstream { @@ -41,14 +42,14 @@ protected void subscribeActual(Subscriber s) { R r; try { - r = ObjectHelper.requireNonNull(seedSupplier.get(), "The seed supplied is null"); + r = Objects.requireNonNull(seedSupplier.get(), "The seed supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptySubscription.error(e, s); return; } - source.subscribe(new ScanSeedSubscriber(s, accumulator, r, bufferSize())); + source.subscribe(new ScanSeedSubscriber<>(s, accumulator, r, bufferSize())); } static final class ScanSeedSubscriber @@ -85,7 +86,7 @@ static final class ScanSeedSubscriber this.value = value; this.prefetch = prefetch; this.limit = prefetch - (prefetch >> 2); - this.queue = new SpscArrayQueue(prefetch); + this.queue = new SpscArrayQueue<>(prefetch); this.queue.offer(value); this.requested = new AtomicLong(); } @@ -109,7 +110,7 @@ public void onNext(T t) { R v = value; try { - v = ObjectHelper.requireNonNull(accumulator.apply(v, t), "The accumulator returned a null value"); + v = Objects.requireNonNull(accumulator.apply(v, t), "The accumulator returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqual.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqual.java index 05be9542be..8dadbe0161 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqual.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqual.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,10 +20,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.BiPredicate; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; public final class FlowableSequenceEqual extends Flowable { final Publisher first; @@ -41,7 +42,7 @@ public FlowableSequenceEqual(Publisher first, Publisher s) { - EqualCoordinator parent = new EqualCoordinator(s, prefetch, comparer); + EqualCoordinator parent = new EqualCoordinator<>(s, prefetch, comparer); s.onSubscribe(parent); parent.subscribe(first, second); } @@ -79,8 +80,8 @@ static final class EqualCoordinator extends DeferredScalarSubscription(this, prefetch); - this.second = new EqualSubscriber(this, prefetch); + this.first = new EqualSubscriber<>(this, prefetch); + this.second = new EqualSubscriber<>(this, prefetch); this.errors = new AtomicThrowable(); } @@ -289,7 +290,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); s.request(prefetch); } @@ -299,7 +300,7 @@ public void onSubscribe(Subscription s) { public void onNext(T t) { if (sourceMode == QueueSubscription.NONE) { if (!queue.offer(t)) { - onError(new MissingBackpressureException()); + onError(MissingBackpressureException.createDefault()); return; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualSingle.java index 7761c93041..5ede5990f9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,6 +25,7 @@ import io.reactivex.rxjava3.internal.operators.flowable.FlowableSequenceEqual.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SimpleQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableSequenceEqualSingle extends Single implements FuseToFlowable { @@ -43,14 +44,14 @@ public FlowableSequenceEqualSingle(Publisher first, Publisher observer) { - EqualCoordinator parent = new EqualCoordinator(observer, prefetch, comparer); + EqualCoordinator parent = new EqualCoordinator<>(observer, prefetch, comparer); observer.onSubscribe(parent); parent.subscribe(first, second); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableSequenceEqual(first, second, comparer, prefetch)); + return RxJavaPlugins.onAssembly(new FlowableSequenceEqual<>(first, second, comparer, prefetch)); } static final class EqualCoordinator @@ -76,8 +77,8 @@ static final class EqualCoordinator EqualCoordinator(SingleObserver actual, int prefetch, BiPredicate comparer) { this.downstream = actual; this.comparer = comparer; - this.first = new EqualSubscriber(this, prefetch); - this.second = new EqualSubscriber(this, prefetch); + this.first = new EqualSubscriber<>(this, prefetch); + this.second = new EqualSubscriber<>(this, prefetch); this.errors = new AtomicThrowable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerialized.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerialized.java index ec924bb0d1..4c43ff9c6f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerialized.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerialized.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.Subscriber; @@ -24,6 +25,6 @@ public FlowableSerialized(Flowable source) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SerializedSubscriber(s)); + source.subscribe(new SerializedSubscriber<>(s)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingle.java index 11a27d372b..02077e5444 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public FlowableSingle(Flowable source, T defaultValue, boolean failOnEmpty) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SingleElementSubscriber(s, defaultValue, failOnEmpty)); + source.subscribe(new SingleElementSubscriber<>(s, defaultValue, failOnEmpty)); } static final class SingleElementSubscriber extends DeferredScalarSubscription diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleMaybe.java index 1d701014cb..51beeff27c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,12 +31,12 @@ public FlowableSingleMaybe(Flowable source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new SingleElementSubscriber(observer)); + source.subscribe(new SingleElementSubscriber<>(observer)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableSingle(source, null, false)); + return RxJavaPlugins.onAssembly(new FlowableSingle<>(source, null, false)); } static final class SingleElementSubscriber diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle.java index 50c4d06ccc..8a4f7fe339 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,12 +36,12 @@ public FlowableSingleSingle(Flowable source, T defaultValue) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new SingleElementSubscriber(observer, defaultValue)); + source.subscribe(new SingleElementSubscriber<>(observer, defaultValue)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableSingle(source, defaultValue, true)); + return RxJavaPlugins.onAssembly(new FlowableSingle<>(source, defaultValue, true)); } static final class SingleElementSubscriber diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkip.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkip.java index f60d36e8f1..d9c2f49d1e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkip.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkip.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public FlowableSkip(Flowable source, long n) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SkipSubscriber(s, n)); + source.subscribe(new SkipSubscriber<>(s, n)); } static final class SkipSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLast.java index 8b3a6a2487..9f19ee86ba 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLast.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public FlowableSkipLast(Flowable source, int skip) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SkipLastSubscriber(s, skip)); + source.subscribe(new SkipLastSubscriber<>(s, skip)); } static final class SkipLastSubscriber extends ArrayDeque implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimed.java index 099c5ec59c..66f7d240f5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,9 +19,9 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class FlowableSkipLastTimed extends AbstractFlowableWithUpstream { final long time; @@ -41,7 +41,7 @@ public FlowableSkipLastTimed(Flowable source, long time, TimeUnit unit, Sched @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SkipLastTimedSubscriber(s, time, unit, scheduler, bufferSize, delayError)); + source.subscribe(new SkipLastTimedSubscriber<>(s, time, unit, scheduler, bufferSize, delayError)); } static final class SkipLastTimedSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { @@ -68,7 +68,7 @@ static final class SkipLastTimedSubscriber extends AtomicInteger implements F this.time = time; this.unit = unit; this.scheduler = scheduler; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.delayError = delayError; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntil.java index 8052bc7fbf..a7c51f9941 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,9 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; public final class FlowableSkipUntil extends AbstractFlowableWithUpstream { final Publisher other; @@ -31,7 +31,7 @@ public FlowableSkipUntil(Flowable source, Publisher other) { @Override protected void subscribeActual(Subscriber child) { - SkipUntilMainSubscriber parent = new SkipUntilMainSubscriber(child); + SkipUntilMainSubscriber parent = new SkipUntilMainSubscriber<>(child); child.onSubscribe(parent); other.subscribe(parent.other); @@ -57,7 +57,7 @@ static final class SkipUntilMainSubscriber extends AtomicInteger SkipUntilMainSubscriber(Subscriber downstream) { this.downstream = downstream; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.requested = new AtomicLong(); this.other = new OtherSubscriber(); this.error = new AtomicThrowable(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhile.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhile.java index 7ed91df231..be88184f38 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhile.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhile.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public FlowableSkipWhile(Flowable source, Predicate predicate) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SkipWhileSubscriber(s, predicate)); + source.subscribe(new SkipWhileSubscriber<>(s, predicate)); } static final class SkipWhileSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn.java index 63394a09ab..7d4a51f0f2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ public FlowableSubscribeOn(Flowable source, Scheduler scheduler, boolean nonS @Override public void subscribeActual(final Subscriber s) { Scheduler.Worker w = scheduler.createWorker(); - final SubscribeOnSubscriber sos = new SubscribeOnSubscriber(s, w, source, nonScheduledRequests); + final SubscribeOnSubscriber sos = new SubscribeOnSubscriber<>(s, w, source, nonScheduledRequests); s.onSubscribe(sos); w.schedule(sos); @@ -69,7 +69,7 @@ static final class SubscribeOnSubscriber extends AtomicReference this.downstream = actual; this.worker = worker; this.source = source; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.requested = new AtomicLong(); this.nonScheduledRequests = !requestOn; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmpty.java index a2e81eac58..1d45216f14 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public FlowableSwitchIfEmpty(Flowable source, Publisher other) { @Override protected void subscribeActual(Subscriber s) { - SwitchIfEmptySubscriber parent = new SwitchIfEmptySubscriber(s, other); + SwitchIfEmptySubscriber parent = new SwitchIfEmptySubscriber<>(s, other); s.onSubscribe(parent.arbiter); source.subscribe(parent); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java index e77110b6a0..4d4521c655 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -20,11 +21,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class FlowableSwitchMap extends AbstractFlowableWithUpstream { @@ -46,7 +47,7 @@ protected void subscribeActual(Subscriber s) { if (FlowableScalarXMap.tryScalarXMapSubscribe(source, s, mapper)) { return; } - source.subscribe(new SwitchMapSubscriber(s, mapper, bufferSize, delayErrors)); + source.subscribe(new SwitchMapSubscriber<>(s, mapper, bufferSize, delayErrors)); } static final class SwitchMapSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { @@ -64,13 +65,13 @@ static final class SwitchMapSubscriber extends AtomicInteger implements Fl Subscription upstream; - final AtomicReference> active = new AtomicReference>(); + final AtomicReference> active = new AtomicReference<>(); final AtomicLong requested = new AtomicLong(); static final SwitchMapInnerSubscriber CANCELLED; static { - CANCELLED = new SwitchMapInnerSubscriber(null, -1L, 1); + CANCELLED = new SwitchMapInnerSubscriber<>(null, -1L, 1); CANCELLED.cancel(); } @@ -110,7 +111,7 @@ public void onNext(T t) { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(t), "The publisher returned is null"); + p = Objects.requireNonNull(mapper.apply(t), "The publisher returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.cancel(); @@ -118,7 +119,7 @@ public void onNext(T t) { return; } - SwitchMapInnerSubscriber nextInner = new SwitchMapInnerSubscriber(this, c, bufferSize); + SwitchMapInnerSubscriber nextInner = new SwitchMapInnerSubscriber<>(this, c, bufferSize); for (;;) { inner = active.get(); @@ -180,12 +181,9 @@ public void cancel() { @SuppressWarnings("unchecked") void disposeInner() { - SwitchMapInnerSubscriber a = active.get(); - if (a != CANCELLED) { - a = active.getAndSet((SwitchMapInnerSubscriber)CANCELLED); - if (a != CANCELLED && a != null) { - a.cancel(); - } + SwitchMapInnerSubscriber a = active.getAndSet((SwitchMapInnerSubscriber)CANCELLED); + if (a != CANCELLED && a != null) { + a.cancel(); } } @@ -201,7 +199,6 @@ void drain() { for (;;) { if (cancelled) { - active.lazySet(null); return; } @@ -228,26 +225,6 @@ void drain() { SwitchMapInnerSubscriber inner = active.get(); SimpleQueue q = inner != null ? inner.queue : null; if (q != null) { - if (inner.done) { - if (!delayErrors) { - Throwable err = errors.get(); - if (err != null) { - disposeInner(); - errors.tryTerminateConsumer(a); - return; - } else - if (q.isEmpty()) { - active.compareAndSet(inner, null); - continue; - } - } else { - if (q.isEmpty()) { - active.compareAndSet(inner, null); - continue; - } - } - } - long r = requested.get(); long e = 0L; boolean retry = false; @@ -306,6 +283,28 @@ void drain() { e++; } + if (e == r) { + if (inner.done) { + if (!delayErrors) { + Throwable err = errors.get(); + if (err != null) { + disposeInner(); + errors.tryTerminateConsumer(a); + return; + } else + if (q.isEmpty()) { + active.compareAndSet(inner, null); + continue; + } + } else { + if (q.isEmpty()) { + active.compareAndSet(inner, null); + continue; + } + } + } + } + if (e != 0L) { if (!cancelled) { if (r != Long.MAX_VALUE) { @@ -371,7 +370,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(bufferSize); + queue = new SpscArrayQueue<>(bufferSize); s.request(bufferSize); } @@ -382,7 +381,7 @@ public void onNext(R t) { SwitchMapSubscriber p = parent; if (index == p.unique) { if (fusionMode == QueueSubscription.NONE && !queue.offer(t)) { - onError(new MissingBackpressureException("Queue full?!")); + onError(new QueueOverflowException()); return; } p.drain(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTake.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTake.java index 9b8e995322..ea5ca1c4e1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTake.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTake.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public FlowableTake(Flowable source, long n) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new TakeSubscriber(s, n)); + source.subscribe(new TakeSubscriber<>(s, n)); } static final class TakeSubscriber @@ -105,12 +105,7 @@ public void request(long n) { if (r == 0L) { break; } - long toRequest; - if (r <= n) { - toRequest = r; - } else { - toRequest = n; - } + long toRequest = Math.min(r, n); long u = r - toRequest; if (compareAndSet(r, u)) { upstream.request(toRequest); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java index 5252bd808d..ad26f45124 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLast.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public FlowableTakeLast(Flowable source, int count) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new TakeLastSubscriber(s, count)); + source.subscribe(new TakeLastSubscriber<>(s, count)); } static final class TakeLastSubscriber extends ArrayDeque implements FlowableSubscriber, Subscription { @@ -119,8 +119,12 @@ void drain() { a.onNext(v); e++; } - if (e != 0L && r != Long.MAX_VALUE) { - r = requested.addAndGet(-e); + if (isEmpty()) { + a.onComplete(); + return; + } + if (e != 0L) { + r = BackpressureHelper.produced(requested, e); } } } while (wip.decrementAndGet() != 0); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOne.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOne.java index fb11b74c0c..43d3bf4446 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOne.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOne.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.reactivestreams.*; @@ -25,7 +26,7 @@ public FlowableTakeLastOne(Flowable source) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new TakeLastOneSubscriber(s)); + source.subscribe(new TakeLastOneSubscriber<>(s)); } static final class TakeLastOneSubscriber extends DeferredScalarSubscription diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimed.java index 2538fc178e..b42b5717ce 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,9 +19,9 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class FlowableTakeLastTimed extends AbstractFlowableWithUpstream { final long count; @@ -45,7 +45,7 @@ public FlowableTakeLastTimed(Flowable source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new TakeLastTimedSubscriber(s, count, time, unit, scheduler, bufferSize, delayError)); + source.subscribe(new TakeLastTimedSubscriber<>(s, count, time, unit, scheduler, bufferSize, delayError)); } static final class TakeLastTimedSubscriber extends AtomicInteger implements FlowableSubscriber, Subscription { @@ -74,7 +74,7 @@ static final class TakeLastTimedSubscriber extends AtomicInteger implements F this.time = time; this.unit = unit; this.scheduler = scheduler; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.delayError = delayError; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakePublisher.java index bda003e7da..b105b34ca2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakePublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakePublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,6 +35,6 @@ public FlowableTakePublisher(Publisher source, long limit) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new TakeSubscriber(s, limit)); + source.subscribe(new TakeSubscriber<>(s, limit)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntil.java index 1bbe14cd57..6ea62f6975 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public FlowableTakeUntil(Flowable source, Publisher other) { @Override protected void subscribeActual(Subscriber child) { - TakeUntilMainSubscriber parent = new TakeUntilMainSubscriber(child); + TakeUntilMainSubscriber parent = new TakeUntilMainSubscriber<>(child); child.onSubscribe(parent); other.subscribe(parent.other); @@ -55,7 +55,7 @@ static final class TakeUntilMainSubscriber extends AtomicInteger implements F TakeUntilMainSubscriber(Subscriber downstream) { this.downstream = downstream; this.requested = new AtomicLong(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.other = new OtherSubscriber(); this.error = new AtomicThrowable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicate.java index 8bb8f27531..a57bb14c0a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public FlowableTakeUntilPredicate(Flowable source, Predicate predi @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new InnerSubscriber(s, predicate)); + source.subscribe(new InnerSubscriber<>(s, predicate)); } static final class InnerSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhile.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhile.java index ba9a115da8..ea8d4f13eb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhile.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhile.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public FlowableTakeWhile(Flowable source, Predicate predicate) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new TakeWhileSubscriber(s, predicate)); + source.subscribe(new TakeWhileSubscriber<>(s, predicate)); } static final class TakeWhileSubscriber implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTimed.java index 4b1441b731..44499ec255 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; @@ -32,44 +34,53 @@ public final class FlowableThrottleFirstTimed extends AbstractFlowableWithUps final long timeout; final TimeUnit unit; final Scheduler scheduler; + final Consumer onDropped; - public FlowableThrottleFirstTimed(Flowable source, long timeout, TimeUnit unit, Scheduler scheduler) { + public FlowableThrottleFirstTimed(Flowable source, + long timeout, + TimeUnit unit, + Scheduler scheduler, + Consumer onDropped) { super(source); this.timeout = timeout; this.unit = unit; this.scheduler = scheduler; + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new DebounceTimedSubscriber( - new SerializedSubscriber(s), - timeout, unit, scheduler.createWorker())); + source.subscribe(new DebounceTimedSubscriber<>( + new SerializedSubscriber<>(s), + timeout, unit, scheduler.createWorker(), + onDropped)); } static final class DebounceTimedSubscriber extends AtomicLong implements FlowableSubscriber, Subscription, Runnable { - private static final long serialVersionUID = -9102637559663639004L; + final Subscriber downstream; final long timeout; final TimeUnit unit; final Scheduler.Worker worker; - + final Consumer onDropped; Subscription upstream; - final SequentialDisposable timer = new SequentialDisposable(); - volatile boolean gate; - boolean done; - DebounceTimedSubscriber(Subscriber actual, long timeout, TimeUnit unit, Worker worker) { + DebounceTimedSubscriber(Subscriber actual, + long timeout, + TimeUnit unit, + Worker worker, + Consumer onDropped) { this.downstream = actual; this.timeout = timeout; this.unit = unit; this.worker = worker; + this.onDropped = onDropped; } @Override @@ -94,9 +105,10 @@ public void onNext(T t) { downstream.onNext(t); BackpressureHelper.produced(this, 1); } else { + upstream.cancel(); done = true; - cancel(); - downstream.onError(new MissingBackpressureException("Could not deliver value due to lack of requests")); + downstream.onError(MissingBackpressureException.createDefault()); + worker.dispose(); return; } @@ -106,6 +118,16 @@ public void onNext(T t) { } timer.replace(worker.schedule(this, timeout, unit)); + } else if (onDropped != null) { + try { + onDropped.accept(t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + done = true; + downstream.onError(ex); + worker.dispose(); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatest.java index fe32addcc6..e28e5f09df 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,9 +19,11 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * Emits the next or latest item when the given time elapses. @@ -44,19 +46,24 @@ public final class FlowableThrottleLatest extends AbstractFlowableWithUpstrea final boolean emitLast; + final Consumer onDropped; + public FlowableThrottleLatest(Flowable source, - long timeout, TimeUnit unit, Scheduler scheduler, - boolean emitLast) { + long timeout, TimeUnit unit, + Scheduler scheduler, + boolean emitLast, + Consumer onDropped) { super(source); this.timeout = timeout; this.unit = unit; this.scheduler = scheduler; this.emitLast = emitLast; + this.onDropped = onDropped; } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ThrottleLatestSubscriber(s, timeout, unit, scheduler.createWorker(), emitLast)); + source.subscribe(new ThrottleLatestSubscriber<>(s, timeout, unit, scheduler.createWorker(), emitLast, onDropped)); } static final class ThrottleLatestSubscriber @@ -79,6 +86,8 @@ static final class ThrottleLatestSubscriber final AtomicLong requested; + final Consumer onDropped; + Subscription upstream; volatile boolean done; @@ -93,15 +102,18 @@ static final class ThrottleLatestSubscriber boolean timerRunning; ThrottleLatestSubscriber(Subscriber downstream, - long timeout, TimeUnit unit, Scheduler.Worker worker, - boolean emitLast) { + long timeout, TimeUnit unit, + Scheduler.Worker worker, + boolean emitLast, + Consumer onDropped) { this.downstream = downstream; this.timeout = timeout; this.unit = unit; this.worker = worker; this.emitLast = emitLast; - this.latest = new AtomicReference(); + this.latest = new AtomicReference<>(); this.requested = new AtomicLong(); + this.onDropped = onDropped; } @Override @@ -115,7 +127,17 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { - latest.set(t); + T old = latest.getAndSet(t); + if (onDropped != null && old != null) { + try { + onDropped.accept(old); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + error = ex; + done = true; + } + } drain(); } @@ -145,6 +167,22 @@ public void cancel() { upstream.cancel(); worker.dispose(); if (getAndIncrement() == 0) { + clear(); + } + } + + void clear() { + if (onDropped != null) { + T v = latest.getAndSet(null); + if (v != null) { + try { + onDropped.accept(v); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + } else { latest.lazySet(null); } } @@ -170,14 +208,27 @@ void drain() { for (;;) { if (cancelled) { - latest.lazySet(null); + clear(); return; } boolean d = done; + Throwable error = this.error; if (d && error != null) { - latest.lazySet(null); + if (onDropped != null) { + T v = latest.getAndSet(null); + if (v != null) { + try { + onDropped.accept(v); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + error = new CompositeException(error, ex); + } + } + } else { + latest.lazySet(null); + } downstream.onError(error); worker.dispose(); return; @@ -187,19 +238,31 @@ void drain() { boolean empty = v == null; if (d) { - if (!empty && emitLast) { + if (!empty) { v = latest.getAndSet(null); - long e = emitted; - if (e != requested.get()) { - emitted = e + 1; - downstream.onNext(v); - downstream.onComplete(); + if (emitLast) { + long e = emitted; + if (e != requested.get()) { + emitted = e + 1; + downstream.onNext(v); + downstream.onComplete(); + } else { + tryDropAndSignalMBE(v); + } } else { - downstream.onError(new MissingBackpressureException( - "Could not emit final value due to lack of requests")); + if (onDropped != null) { + try { + onDropped.accept(v); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + worker.dispose(); + return; + } + } + downstream.onComplete(); } } else { - latest.lazySet(null); downstream.onComplete(); } worker.dispose(); @@ -222,8 +285,7 @@ void drain() { emitted = e + 1; } else { upstream.cancel(); - downstream.onError(new MissingBackpressureException( - "Could not emit value due to lack of requests")); + tryDropAndSignalMBE(v); worker.dispose(); return; } @@ -242,5 +304,18 @@ void drain() { } } } + + void tryDropAndSignalMBE(T valueToDrop) { + Throwable errorToSignal = MissingBackpressureException.createDefault(); + if (onDropped != null) { + try { + onDropped.accept(valueToDrop); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + errorToSignal = new CompositeException(errorToSignal, ex); + } + } + downstream.onError(errorToSignal); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeInterval.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeInterval.java index 4f7c1e899b..1601814c98 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeInterval.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeInterval.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public FlowableTimeInterval(Flowable source, TimeUnit unit, Scheduler schedul @Override protected void subscribeActual(Subscriber> s) { - source.subscribe(new TimeIntervalSubscriber(s, unit, scheduler)); + source.subscribe(new TimeIntervalSubscriber<>(s, unit, scheduler)); } static final class TimeIntervalSubscriber implements FlowableSubscriber, Subscription { @@ -66,7 +66,7 @@ public void onNext(T t) { long last = lastTime; lastTime = now; long delta = now - last; - downstream.onNext(new Timed(t, delta, unit)); + downstream.onNext(new Timed<>(t, delta, unit)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeout.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeout.java index b45755fc30..506132f595 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeout.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeout.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.*; @@ -23,7 +24,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.flowable.FlowableTimeoutTimed.TimeoutSupport; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -47,12 +47,12 @@ public FlowableTimeout( @Override protected void subscribeActual(Subscriber s) { if (other == null) { - TimeoutSubscriber parent = new TimeoutSubscriber(s, itemTimeoutIndicator); + TimeoutSubscriber parent = new TimeoutSubscriber<>(s, itemTimeoutIndicator); s.onSubscribe(parent); parent.startFirstTimeout(firstTimeoutIndicator); source.subscribe(parent); } else { - TimeoutFallbackSubscriber parent = new TimeoutFallbackSubscriber(s, itemTimeoutIndicator, other); + TimeoutFallbackSubscriber parent = new TimeoutFallbackSubscriber<>(s, itemTimeoutIndicator, other); s.onSubscribe(parent); parent.startFirstTimeout(firstTimeoutIndicator); source.subscribe(parent); @@ -82,7 +82,7 @@ static final class TimeoutSubscriber extends AtomicLong this.downstream = actual; this.itemTimeoutIndicator = itemTimeoutIndicator; this.task = new SequentialDisposable(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.requested = new AtomicLong(); } @@ -108,7 +108,7 @@ public void onNext(T t) { Publisher itemTimeoutPublisher; try { - itemTimeoutPublisher = ObjectHelper.requireNonNull( + itemTimeoutPublisher = Objects.requireNonNull( itemTimeoutIndicator.apply(t), "The itemTimeoutIndicator returned a null Publisher."); } catch (Throwable ex) { @@ -212,7 +212,7 @@ static final class TimeoutFallbackSubscriber extends SubscriptionArbiter this.downstream = actual; this.itemTimeoutIndicator = itemTimeoutIndicator; this.task = new SequentialDisposable(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.fallback = fallback; this.index = new AtomicLong(); } @@ -243,7 +243,7 @@ public void onNext(T t) { Publisher itemTimeoutPublisher; try { - itemTimeoutPublisher = ObjectHelper.requireNonNull( + itemTimeoutPublisher = Objects.requireNonNull( itemTimeoutIndicator.apply(t), "The itemTimeoutIndicator returned a null Publisher."); } catch (Throwable ex) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTimed.java index 8963788992..67d612011d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -43,12 +43,12 @@ public FlowableTimeoutTimed(Flowable source, @Override protected void subscribeActual(Subscriber s) { if (other == null) { - TimeoutSubscriber parent = new TimeoutSubscriber(s, timeout, unit, scheduler.createWorker()); + TimeoutSubscriber parent = new TimeoutSubscriber<>(s, timeout, unit, scheduler.createWorker()); s.onSubscribe(parent); parent.startTimeout(0L); source.subscribe(parent); } else { - TimeoutFallbackSubscriber parent = new TimeoutFallbackSubscriber(s, timeout, unit, scheduler.createWorker(), other); + TimeoutFallbackSubscriber parent = new TimeoutFallbackSubscriber<>(s, timeout, unit, scheduler.createWorker(), other); s.onSubscribe(parent); parent.startTimeout(0L); source.subscribe(parent); @@ -80,7 +80,7 @@ static final class TimeoutSubscriber extends AtomicLong this.unit = unit; this.worker = worker; this.task = new SequentialDisposable(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.requested = new AtomicLong(); } @@ -203,7 +203,7 @@ static final class TimeoutFallbackSubscriber extends SubscriptionArbiter this.worker = worker; this.fallback = fallback; this.task = new SequentialDisposable(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.index = new AtomicLong(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimer.java index 6c172320de..db45fe2e98 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -78,7 +78,7 @@ public void run() { downstream.onComplete(); } else { lazySet(EmptyDisposable.INSTANCE); - downstream.onError(new MissingBackpressureException("Can't deliver value due to lack of requests")); + downstream.onError(MissingBackpressureException.createDefault()); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToList.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToList.java index 439fba1074..3f03d93112 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToList.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToList.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,12 +41,12 @@ protected void subscribeActual(Subscriber s) { EmptySubscription.error(e, s); return; } - source.subscribe(new ToListSubscriber(s, coll)); + source.subscribe(new ToListSubscriber<>(s, coll)); } static final class ToListSubscriber> extends DeferredScalarSubscription - implements FlowableSubscriber, Subscription { + implements FlowableSubscriber { private static final long serialVersionUID = -8134157938864266736L; Subscription upstream; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListSingle.java index b90869ad62..5e65b48e08 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,12 +53,12 @@ protected void subscribeActual(SingleObserver observer) { EmptyDisposable.error(e, observer); return; } - source.subscribe(new ToListSubscriber(observer, coll)); + source.subscribe(new ToListSubscriber<>(observer, coll)); } @Override public Flowable fuseToFlowable() { - return RxJavaPlugins.onAssembly(new FlowableToList(source, collectionSupplier)); + return RxJavaPlugins.onAssembly(new FlowableToList<>(source, collectionSupplier)); } static final class ToListSubscriber> diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOn.java index 3a7d29a47b..dd8c4ce4cf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public FlowableUnsubscribeOn(Flowable source, Scheduler scheduler) { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new UnsubscribeSubscriber(s, scheduler)); + source.subscribe(new UnsubscribeSubscriber<>(s, scheduler)); } static final class UnsubscribeSubscriber extends AtomicBoolean implements FlowableSubscriber, Subscription { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java index 9c0dc74d4c..8f34a6e414 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsing.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import org.reactivestreams.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -54,7 +54,7 @@ public void subscribeActual(Subscriber s) { Publisher source; try { - source = ObjectHelper.requireNonNull(sourceSupplier.apply(resource), "The sourceSupplier returned a null Publisher"); + source = Objects.requireNonNull(sourceSupplier.apply(resource), "The sourceSupplier returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); try { @@ -68,7 +68,7 @@ public void subscribeActual(Subscriber s) { return; } - UsingSubscriber us = new UsingSubscriber(s, resource, disposer, eager); + UsingSubscriber us = new UsingSubscriber<>(s, resource, disposer, eager); source.subscribe(us); } @@ -117,7 +117,6 @@ public void onError(Throwable t) { } } - upstream.cancel(); if (innerError != null) { downstream.onError(new CompositeException(t, innerError)); } else { @@ -125,7 +124,6 @@ public void onError(Throwable t) { } } else { downstream.onError(t); - upstream.cancel(); disposeResource(); } } @@ -143,11 +141,9 @@ public void onComplete() { } } - upstream.cancel(); downstream.onComplete(); } else { downstream.onComplete(); - upstream.cancel(); disposeResource(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java index f66d38169e..85ab5a5e0b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindow.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,11 +19,10 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.processors.UnicastProcessor; public final class FlowableWindow extends AbstractFlowableWithUpstream> { final long size; @@ -42,12 +41,12 @@ public FlowableWindow(Flowable source, long size, long skip, int bufferSize) @Override public void subscribeActual(Subscriber> s) { if (skip == size) { - source.subscribe(new WindowExactSubscriber(s, size, bufferSize)); + source.subscribe(new WindowExactSubscriber<>(s, size, bufferSize)); } else if (skip > size) { - source.subscribe(new WindowSkipSubscriber(s, size, skip, bufferSize)); + source.subscribe(new WindowSkipSubscriber<>(s, size, skip, bufferSize)); } else { - source.subscribe(new WindowOverlapSubscriber(s, size, skip, bufferSize)); + source.subscribe(new WindowOverlapSubscriber<>(s, size, skip, bufferSize)); } } @@ -92,14 +91,14 @@ public void onNext(T t) { long i = index; UnicastProcessor w = window; - WindowSubscribeIntercept intercept = null; + FlowableWindowSubscribeIntercept intercept = null; if (i == 0) { getAndIncrement(); - w = UnicastProcessor.create(bufferSize, this); + w = UnicastProcessor.create(bufferSize, this); window = w; - intercept = new WindowSubscribeIntercept(w); + intercept = new FlowableWindowSubscribeIntercept<>(w); downstream.onNext(intercept); } @@ -211,15 +210,15 @@ public void onSubscribe(Subscription s) { public void onNext(T t) { long i = index; - WindowSubscribeIntercept intercept = null; + FlowableWindowSubscribeIntercept intercept = null; UnicastProcessor w = window; if (i == 0) { getAndIncrement(); - w = UnicastProcessor.create(bufferSize, this); + w = UnicastProcessor.create(bufferSize, this); window = w; - intercept = new WindowSubscribeIntercept(w); + intercept = new FlowableWindowSubscribeIntercept<>(w); downstream.onNext(intercept); } @@ -339,8 +338,8 @@ static final class WindowOverlapSubscriber this.downstream = actual; this.size = size; this.skip = skip; - this.queue = new SpscLinkedArrayQueue>(bufferSize); - this.windows = new ArrayDeque>(); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); + this.windows = new ArrayDeque<>(); this.once = new AtomicBoolean(); this.firstRequest = new AtomicBoolean(); this.requested = new AtomicLong(); @@ -358,10 +357,6 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { - if (done) { - return; - } - long i = index; UnicastProcessor newWindow = null; @@ -369,7 +364,7 @@ public void onNext(T t) { if (!cancelled) { getAndIncrement(); - newWindow = UnicastProcessor.create(bufferSize, this); + newWindow = UnicastProcessor.create(bufferSize, this); windows.offer(newWindow); } @@ -407,11 +402,6 @@ public void onNext(T t) { @Override public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; - } - for (Processor w : windows) { w.onError(t); } @@ -424,10 +414,6 @@ public void onError(Throwable t) { @Override public void onComplete() { - if (done) { - return; - } - for (Processor w : windows) { w.onComplete(); } @@ -477,7 +463,7 @@ void drain() { break; } - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(t); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(t); a.onNext(intercept); if (intercept.tryAbandon()) { @@ -488,7 +474,7 @@ void drain() { if (e == r) { if (cancelled) { - continue outer; + continue; } if (checkTerminated(done, q.isEmpty(), a, q)) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundary.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundary.java index ba43da6d84..2aad69dcb5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundary.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundary.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public FlowableWindowBoundary(Flowable source, Publisher other, int capaci @Override protected void subscribeActual(Subscriber> subscriber) { - WindowBoundaryMainSubscriber parent = new WindowBoundaryMainSubscriber(subscriber, capacityHint); + WindowBoundaryMainSubscriber parent = new WindowBoundaryMainSubscriber<>(subscriber, capacityHint); subscriber.onSubscribe(parent); @@ -84,10 +84,10 @@ static final class WindowBoundaryMainSubscriber WindowBoundaryMainSubscriber(Subscriber> downstream, int capacityHint) { this.downstream = downstream; this.capacityHint = capacityHint; - this.boundarySubscriber = new WindowBoundaryInnerSubscriber(this); - this.upstream = new AtomicReference(); + this.boundarySubscriber = new WindowBoundaryInnerSubscriber<>(this); + this.upstream = new AtomicReference<>(); this.windows = new AtomicInteger(1); - this.queue = new MpscLinkedQueue(); + this.queue = new MpscLinkedQueue<>(); this.errors = new AtomicThrowable(); this.stopWindows = new AtomicBoolean(); this.requested = new AtomicLong(); @@ -240,11 +240,15 @@ void drain() { if (emitted != requested.get()) { emitted++; - downstream.onNext(w); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(w); + downstream.onNext(intercept); + if (intercept.tryAbandon()) { + w.onComplete(); + } } else { SubscriptionHelper.cancel(upstream); boundarySubscriber.dispose(); - errors.tryAddThrowableOrReport(new MissingBackpressureException("Could not deliver a window due to lack of requests")); + errors.tryAddThrowableOrReport(MissingBackpressureException.createDefault()); done = true; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundarySelector.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundarySelector.java index 573f0257e9..d6bffae2e1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundarySelector.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowBoundarySelector.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,70 +18,84 @@ import org.reactivestreams.*; -import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; -import io.reactivex.rxjava3.internal.subscribers.QueueDrainSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; -import io.reactivex.rxjava3.internal.util.NotificationLite; +import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.UnicastProcessor; -import io.reactivex.rxjava3.subscribers.*; public final class FlowableWindowBoundarySelector extends AbstractFlowableWithUpstream> { final Publisher open; - final Function> close; + final Function> closingIndicator; final int bufferSize; public FlowableWindowBoundarySelector( Flowable source, - Publisher open, Function> close, + Publisher open, Function> closingIndicator, int bufferSize) { super(source); this.open = open; - this.close = close; + this.closingIndicator = closingIndicator; this.bufferSize = bufferSize; } @Override protected void subscribeActual(Subscriber> s) { - source.subscribe(new WindowBoundaryMainSubscriber( - new SerializedSubscriber>(s), - open, close, bufferSize)); + source.subscribe(new WindowBoundaryMainSubscriber<>( + s, open, closingIndicator, bufferSize)); } static final class WindowBoundaryMainSubscriber - extends QueueDrainSubscriber> - implements Subscription { + extends AtomicInteger + implements FlowableSubscriber, Subscription, Runnable { + private static final long serialVersionUID = 8646217640096099753L; + + final Subscriber> downstream; final Publisher open; - final Function> close; + final Function> closingIndicator; final int bufferSize; final CompositeDisposable resources; - Subscription upstream; + final WindowStartSubscriber startSubscriber; + + final List> windows; + + final SimplePlainQueue queue; + + final AtomicLong windowCount; - final AtomicReference boundary = new AtomicReference(); + final AtomicBoolean downstreamCancelled; - final List> ws; + final AtomicLong requested; + long emitted; - final AtomicLong windows = new AtomicLong(); + volatile boolean upstreamCanceled; - final AtomicBoolean stopWindows = new AtomicBoolean(); + volatile boolean upstreamDone; + volatile boolean openDone; + final AtomicThrowable error; + + Subscription upstream; WindowBoundaryMainSubscriber(Subscriber> actual, - Publisher open, Function> close, int bufferSize) { - super(actual, new MpscLinkedQueue()); + Publisher open, Function> closingIndicator, int bufferSize) { + this.downstream = actual; + this.queue = new MpscLinkedQueue<>(); this.open = open; - this.close = close; + this.closingIndicator = closingIndicator; this.bufferSize = bufferSize; this.resources = new CompositeDisposable(); - this.ws = new ArrayList>(); - windows.lazySet(1); + this.windows = new ArrayList<>(); + this.windowCount = new AtomicLong(1L); + this.downstreamCancelled = new AtomicBoolean(); + this.error = new AtomicThrowable(); + this.startSubscriber = new WindowStartSubscriber<>(this); + this.requested = new AtomicLong(); } @Override @@ -91,297 +105,341 @@ public void onSubscribe(Subscription s) { downstream.onSubscribe(this); - if (stopWindows.get()) { - return; - } - - OperatorWindowBoundaryOpenSubscriber os = new OperatorWindowBoundaryOpenSubscriber(this); + open.subscribe(startSubscriber); - if (boundary.compareAndSet(null, os)) { - s.request(Long.MAX_VALUE); - open.subscribe(os); - } + s.request(Long.MAX_VALUE); } } @Override public void onNext(T t) { - if (done) { - return; - } - if (fastEnter()) { - for (UnicastProcessor w : ws) { - w.onNext(t); - } - if (leave(-1) == 0) { - return; - } - } else { - queue.offer(NotificationLite.next(t)); - if (!enter()) { - return; - } - } - drainLoop(); + queue.offer(t); + drain(); } @Override public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; - } - error = t; - done = true; - - if (enter()) { - drainLoop(); - } - - if (windows.decrementAndGet() == 0) { - resources.dispose(); + startSubscriber.cancel(); + resources.dispose(); + if (error.tryAddThrowableOrReport(t)) { + upstreamDone = true; + drain(); } - - downstream.onError(t); } @Override public void onComplete() { - if (done) { - return; + startSubscriber.cancel(); + resources.dispose(); + upstreamDone = true; + drain(); + } + + @Override + public void request(long n) { + if (SubscriptionHelper.validate(n)) { + BackpressureHelper.add(requested, n); } - done = true; + } - if (enter()) { - drainLoop(); + @Override + public void cancel() { + if (downstreamCancelled.compareAndSet(false, true)) { + if (windowCount.decrementAndGet() == 0) { + upstream.cancel(); + startSubscriber.cancel(); + resources.dispose(); + error.tryTerminateAndReport(); + upstreamCanceled = true; + drain(); + } else { + startSubscriber.cancel(); + } } + } - if (windows.decrementAndGet() == 0) { + @Override + public void run() { + if (windowCount.decrementAndGet() == 0) { + upstream.cancel(); + startSubscriber.cancel(); resources.dispose(); + error.tryTerminateAndReport(); + upstreamCanceled = true; + drain(); } + } - downstream.onComplete(); + void open(B startValue) { + queue.offer(new WindowStartItem<>(startValue)); + drain(); } - void error(Throwable t) { + void openError(Throwable t) { upstream.cancel(); resources.dispose(); - DisposableHelper.dispose(boundary); - - downstream.onError(t); + if (error.tryAddThrowableOrReport(t)) { + upstreamDone = true; + drain(); + } } - @Override - public void request(long n) { - requested(n); + void openComplete() { + openDone = true; + drain(); } - @Override - public void cancel() { - if (stopWindows.compareAndSet(false, true)) { - DisposableHelper.dispose(boundary); - if (windows.decrementAndGet() == 0) { - upstream.cancel(); - } - } + void close(WindowEndSubscriberIntercept what) { + queue.offer(what); + drain(); } - void dispose() { + void closeError(Throwable t) { + upstream.cancel(); + startSubscriber.cancel(); resources.dispose(); - DisposableHelper.dispose(boundary); + if (error.tryAddThrowableOrReport(t)) { + upstreamDone = true; + drain(); + } } - void drainLoop() { - final SimplePlainQueue q = queue; - final Subscriber> a = downstream; - final List> ws = this.ws; + void drain() { + if (getAndIncrement() != 0) { + return; + } + int missed = 1; + final Subscriber> downstream = this.downstream; + final SimplePlainQueue queue = this.queue; + final List> windows = this.windows; for (;;) { - - for (;;) { - boolean d = done; - Object o = q.poll(); - - boolean empty = o == null; - - if (d && empty) { - dispose(); - Throwable e = error; - if (e != null) { - for (UnicastProcessor w : ws) { - w.onError(e); - } - } else { - for (UnicastProcessor w : ws) { - w.onComplete(); - } + if (upstreamCanceled) { + queue.clear(); + windows.clear(); + } else { + boolean isDone = upstreamDone; + Object o = queue.poll(); + boolean isEmpty = o == null; + + if (isDone) { + if (isEmpty || error.get() != null) { + terminateDownstream(downstream); + upstreamCanceled = true; + continue; } - ws.clear(); - return; - } - - if (empty) { - break; } - if (o instanceof WindowOperation) { - @SuppressWarnings("unchecked") - WindowOperation wo = (WindowOperation) o; - - UnicastProcessor w = wo.w; - if (w != null) { - if (ws.remove(wo.w)) { - wo.w.onComplete(); - - if (windows.decrementAndGet() == 0) { - dispose(); - return; + if (!isEmpty) { + if (o instanceof WindowStartItem) { + if (!downstreamCancelled.get()) { + long emitted = this.emitted; + if (requested.get() != emitted) { + this.emitted = ++emitted; + + @SuppressWarnings("unchecked") + B startItem = ((WindowStartItem)o).item; + + Publisher endSource; + try { + endSource = Objects.requireNonNull(closingIndicator.apply(startItem), "The closingIndicator returned a null Publisher"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + startSubscriber.cancel(); + resources.dispose(); + Exceptions.throwIfFatal(ex); + error.tryAddThrowableOrReport(ex); + upstreamDone = true; + continue; + } + + windowCount.getAndIncrement(); + UnicastProcessor newWindow = UnicastProcessor.create(bufferSize, this); + WindowEndSubscriberIntercept endSubscriber = new WindowEndSubscriberIntercept<>(this, newWindow); + + downstream.onNext(endSubscriber); + + if (endSubscriber.tryAbandon()) { + newWindow.onComplete(); + } else { + windows.add(newWindow); + resources.add(endSubscriber); + endSource.subscribe(endSubscriber); + } + } else { + upstream.cancel(); + startSubscriber.cancel(); + resources.dispose(); + error.tryAddThrowableOrReport(FlowableWindowTimed.missingBackpressureMessage(emitted)); + upstreamDone = true; } } - continue; - } - - if (stopWindows.get()) { - continue; } + else if (o instanceof WindowEndSubscriberIntercept) { + @SuppressWarnings("unchecked") + UnicastProcessor w = ((WindowEndSubscriberIntercept)o).window; - w = UnicastProcessor.create(bufferSize); - - long r = requested(); - if (r != 0L) { - ws.add(w); - a.onNext(w); - if (r != Long.MAX_VALUE) { - produced(1); - } + windows.remove(w); + resources.delete((Disposable)o); + w.onComplete(); } else { - cancel(); - a.onError(new MissingBackpressureException("Could not deliver new window due to lack of requests")); - continue; - } - - Publisher p; + @SuppressWarnings("unchecked") + T item = (T)o; - try { - p = ObjectHelper.requireNonNull(close.apply(wo.open), "The publisher supplied is null"); - } catch (Throwable e) { - cancel(); - a.onError(e); - continue; - } - - OperatorWindowBoundaryCloseSubscriber cl = new OperatorWindowBoundaryCloseSubscriber(this, w); - - if (resources.add(cl)) { - windows.getAndIncrement(); - - p.subscribe(cl); + for (UnicastProcessor w : windows) { + w.onNext(item); + } } continue; } - - for (UnicastProcessor w : ws) { - w.onNext(NotificationLite.getValue(o)); + else if (openDone && windows.size() == 0) { + upstream.cancel(); + startSubscriber.cancel(); + resources.dispose(); + terminateDownstream(downstream); + upstreamCanceled = true; + continue; } } - missed = leave(-missed); + missed = addAndGet(-missed); if (missed == 0) { break; } } } - @Override - public boolean accept(Subscriber> a, Object v) { - // not used by this operator - return false; + void terminateDownstream(Subscriber downstream) { + Throwable ex = error.terminate(); + if (ex == null) { + for (UnicastProcessor w : windows) { + w.onComplete(); + } + downstream.onComplete(); + } else if (ex != ExceptionHelper.TERMINATED) { + for (UnicastProcessor w : windows) { + w.onError(ex); + } + downstream.onError(ex); + } } - void open(B b) { - queue.offer(new WindowOperation(null, b)); - if (enter()) { - drainLoop(); + static final class WindowStartItem { + + final B item; + + WindowStartItem(B item) { + this.item = item; } } - void close(OperatorWindowBoundaryCloseSubscriber w) { - resources.delete(w); - queue.offer(new WindowOperation(w.w, null)); - if (enter()) { - drainLoop(); + static final class WindowStartSubscriber extends AtomicReference + implements FlowableSubscriber { + + private static final long serialVersionUID = -3326496781427702834L; + + final WindowBoundaryMainSubscriber parent; + + WindowStartSubscriber(WindowBoundaryMainSubscriber parent) { + this.parent = parent; } - } - } - static final class WindowOperation { - final UnicastProcessor w; - final B open; - WindowOperation(UnicastProcessor w, B open) { - this.w = w; - this.open = open; - } - } + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.setOnce(this, s)) { + s.request(Long.MAX_VALUE); + } + } - static final class OperatorWindowBoundaryOpenSubscriber extends DisposableSubscriber { - final WindowBoundaryMainSubscriber parent; + @Override + public void onNext(B t) { + parent.open(t); + } - OperatorWindowBoundaryOpenSubscriber(WindowBoundaryMainSubscriber parent) { - this.parent = parent; - } + @Override + public void onError(Throwable t) { + parent.openError(t); + } - @Override - public void onNext(B t) { - parent.open(t); - } + @Override + public void onComplete() { + parent.openComplete(); + } - @Override - public void onError(Throwable t) { - parent.error(t); + void cancel() { + SubscriptionHelper.cancel(this); + } } - @Override - public void onComplete() { - parent.onComplete(); - } - } + static final class WindowEndSubscriberIntercept extends Flowable + implements FlowableSubscriber, Disposable { - static final class OperatorWindowBoundaryCloseSubscriber extends DisposableSubscriber { - final WindowBoundaryMainSubscriber parent; - final UnicastProcessor w; + final WindowBoundaryMainSubscriber parent; - boolean done; + final UnicastProcessor window; - OperatorWindowBoundaryCloseSubscriber(WindowBoundaryMainSubscriber parent, UnicastProcessor w) { - this.parent = parent; - this.w = w; - } + final AtomicReference upstream; - @Override - public void onNext(V t) { - cancel(); - onComplete(); - } + final AtomicBoolean once; - @Override - public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; + WindowEndSubscriberIntercept(WindowBoundaryMainSubscriber parent, UnicastProcessor window) { + this.parent = parent; + this.window = window; + this.upstream = new AtomicReference<>(); + this.once = new AtomicBoolean(); } - done = true; - parent.error(t); - } - @Override - public void onComplete() { - if (done) { - return; + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.setOnce(upstream, s)) { + s.request(Long.MAX_VALUE); + } + } + + @Override + public void onNext(V t) { + if (SubscriptionHelper.cancel(upstream)) { + parent.close(this); + } + } + + @Override + public void onError(Throwable t) { + if (isDisposed()) { + RxJavaPlugins.onError(t); + } else { + parent.closeError(t); + } + } + + @Override + public void onComplete() { + parent.close(this); + } + + @Override + public void dispose() { + SubscriptionHelper.cancel(upstream); + } + + @Override + public boolean isDisposed() { + return upstream.get() == SubscriptionHelper.CANCELLED; + } + + @Override + protected void subscribeActual(Subscriber s) { + window.subscribe(s); + once.set(true); + } + + boolean tryAbandon() { + return !once.get() && once.compareAndSet(false, true); } - done = true; - parent.close(this); } } + } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/WindowSubscribeIntercept.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowSubscribeIntercept.java similarity index 90% rename from src/main/java/io/reactivex/rxjava3/internal/operators/flowable/WindowSubscribeIntercept.java rename to src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowSubscribeIntercept.java index 4fcbdee225..8ef2204c97 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/WindowSubscribeIntercept.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowSubscribeIntercept.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,13 +25,13 @@ * @param the element type of the flow. * @since 3.0.0 */ -final class WindowSubscribeIntercept extends Flowable { +final class FlowableWindowSubscribeIntercept extends Flowable { final FlowableProcessor window; final AtomicBoolean once; - WindowSubscribeIntercept(FlowableProcessor source) { + FlowableWindowSubscribeIntercept(FlowableProcessor source) { this.window = source; this.once = new AtomicBoolean(); } @@ -45,4 +45,4 @@ protected void subscribeActual(Subscriber s) { boolean tryAbandon() { return !once.get() && once.compareAndSet(false, true); } -} \ No newline at end of file +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowTimed.java index c340c0e0db..39e7de59ad 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,12 +23,11 @@ import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.exceptions.MissingBackpressureException; import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.SimplePlainQueue; import io.reactivex.rxjava3.processors.UnicastProcessor; -import io.reactivex.rxjava3.subscribers.SerializedSubscriber; public final class FlowableWindowTimed extends AbstractFlowableWithUpstream> { final long timespan; @@ -53,23 +52,21 @@ public FlowableWindowTimed(Flowable source, } @Override - protected void subscribeActual(Subscriber> s) { - SerializedSubscriber> actual = new SerializedSubscriber>(s); - + protected void subscribeActual(Subscriber> downstream) { if (timespan == timeskip) { if (maxSize == Long.MAX_VALUE) { - source.subscribe(new WindowExactUnboundedSubscriber( - actual, + source.subscribe(new WindowExactUnboundedSubscriber<>( + downstream, timespan, unit, scheduler, bufferSize)); return; } - source.subscribe(new WindowExactBoundedSubscriber( - actual, + source.subscribe(new WindowExactBoundedSubscriber<>( + downstream, timespan, unit, scheduler, bufferSize, maxSize, restartTimerOnMaxSize)); return; } - source.subscribe(new WindowSkipSubscriber(actual, + source.subscribe(new WindowSkipSubscriber<>(downstream, timespan, timeskip, unit, scheduler.createWorker(), bufferSize)); } @@ -100,9 +97,9 @@ abstract static class AbstractWindowSubscriber final AtomicInteger windowCount; - AbstractWindowSubscriber(Subscriber> actual, long timespan, TimeUnit unit, int bufferSize) { - this.downstream = actual; - this.queue = new MpscLinkedQueue(); + AbstractWindowSubscriber(Subscriber> downstream, long timespan, TimeUnit unit, int bufferSize) { + this.downstream = downstream; + this.queue = new MpscLinkedQueue<>(); this.timespan = timespan; this.unit = unit; this.bufferSize = bufferSize; @@ -204,7 +201,7 @@ void createFirstWindow() { emitted = 1; - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(window); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(window); downstream.onNext(intercept); timer.replace(scheduler.schedulePeriodicallyDirect(this, timespan, timespan, unit)); @@ -216,7 +213,7 @@ void createFirstWindow() { upstream.request(Long.MAX_VALUE); } else { upstream.cancel(); - downstream.onError(new MissingBackpressureException(missingBackpressureMessage(emitted))); + downstream.onError(missingBackpressureMessage(emitted)); cleanupResources(); upstreamCancelled = true; @@ -285,7 +282,7 @@ else if (!isEmpty) { cleanupResources(); upstreamCancelled = true; - downstream.onError(new MissingBackpressureException(missingBackpressureMessage(emitted))); + downstream.onError(missingBackpressureMessage(emitted)); } else { emitted++; @@ -293,7 +290,7 @@ else if (!isEmpty) { window = UnicastProcessor.create(bufferSize, windowRunnable); this.window = window; - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(window); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(window); downstream.onNext(intercept); if (intercept.tryAbandon()) { @@ -372,7 +369,7 @@ void createFirstWindow() { windowCount.getAndIncrement(); window = UnicastProcessor.create(bufferSize, this); - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(window); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(window); downstream.onNext(intercept); Runnable boundaryTask = new WindowBoundaryRunnable(this, 1L); @@ -389,7 +386,7 @@ void createFirstWindow() { upstream.request(Long.MAX_VALUE); } else { upstream.cancel(); - downstream.onError(new MissingBackpressureException(missingBackpressureMessage(emitted))); + downstream.onError(missingBackpressureMessage(emitted)); cleanupResources(); upstreamCancelled = true; @@ -502,7 +499,7 @@ UnicastProcessor createNewWindow(UnicastProcessor window) { cleanupResources(); upstreamCancelled = true; - downstream.onError(new MissingBackpressureException(missingBackpressureMessage(emitted))); + downstream.onError(missingBackpressureMessage(emitted)); } else { this.emitted = ++emitted; @@ -510,7 +507,7 @@ UnicastProcessor createNewWindow(UnicastProcessor window) { window = UnicastProcessor.create(bufferSize, this); this.window = window; - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(window); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(window); downstream.onNext(intercept); if (restartTimerOnMaxSize) { @@ -560,7 +557,7 @@ static final class WindowSkipSubscriber super(actual, timespan, unit, bufferSize); this.timeskip = timeskip; this.worker = worker; - this.windows = new LinkedList>(); + this.windows = new LinkedList<>(); } @Override @@ -573,7 +570,7 @@ void createFirstWindow() { UnicastProcessor window = UnicastProcessor.create(bufferSize, this); windows.add(window); - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(window); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(window); downstream.onNext(intercept); worker.schedule(new WindowBoundaryRunnable(this, false), timespan, unit); @@ -587,7 +584,7 @@ void createFirstWindow() { upstream.request(Long.MAX_VALUE); } else { upstream.cancel(); - downstream.onError(new MissingBackpressureException(missingBackpressureMessage(emitted))); + downstream.onError(missingBackpressureMessage(emitted)); cleanupResources(); upstreamCancelled = true; @@ -647,7 +644,7 @@ void drain() { UnicastProcessor window = UnicastProcessor.create(bufferSize, this); windows.add(window); - WindowSubscribeIntercept intercept = new WindowSubscribeIntercept(window); + FlowableWindowSubscribeIntercept intercept = new FlowableWindowSubscribeIntercept<>(window); downstream.onNext(intercept); worker.schedule(new WindowBoundaryRunnable(this, false), timespan, unit); @@ -657,7 +654,7 @@ void drain() { } } else { upstream.cancel(); - Throwable ex = new MissingBackpressureException(missingBackpressureMessage(emitted)); + Throwable ex = missingBackpressureMessage(emitted); for (UnicastProcessor window : windows) { window.onError(ex); } @@ -720,8 +717,8 @@ public void run() { } } - static String missingBackpressureMessage(long index) { - return "Unable to emit the next window (#" + index + ") due to lack of requests. Please make sure the downstream is ready to consume windows."; + static MissingBackpressureException missingBackpressureMessage(long index) { + return new MissingBackpressureException("Unable to emit the next window (#" + index + ") due to lack of requests. Please make sure the downstream is ready to consume windows."); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFrom.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFrom.java index c3ec3ab5f9..cad9f3c0ca 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFrom.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFrom.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -20,9 +21,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.subscribers.SerializedSubscriber; public final class FlowableWithLatestFrom extends AbstractFlowableWithUpstream { @@ -36,8 +36,8 @@ public FlowableWithLatestFrom(Flowable source, BiFunction s) { - final SerializedSubscriber serial = new SerializedSubscriber(s); - final WithLatestFromSubscriber wlf = new WithLatestFromSubscriber(serial, combiner); + final SerializedSubscriber serial = new SerializedSubscriber<>(s); + final WithLatestFromSubscriber wlf = new WithLatestFromSubscriber<>(serial, combiner); serial.onSubscribe(wlf); @@ -55,11 +55,11 @@ static final class WithLatestFromSubscriber extends AtomicReference final BiFunction combiner; - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); final AtomicLong requested = new AtomicLong(); - final AtomicReference other = new AtomicReference(); + final AtomicReference other = new AtomicReference<>(); WithLatestFromSubscriber(Subscriber actual, BiFunction combiner) { this.downstream = actual; @@ -84,7 +84,7 @@ public boolean tryOnNext(T t) { if (u != null) { R r; try { - r = ObjectHelper.requireNonNull(combiner.apply(t, u), "The combiner returned a null value"); + r = Objects.requireNonNull(combiner.apply(t, u), "The combiner returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromMany.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromMany.java index 7c3b06ed8c..77d0527729 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromMany.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromMany.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,9 +10,11 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -21,10 +23,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -82,11 +83,11 @@ protected void subscribeActual(Subscriber s) { } if (n == 0) { - new FlowableMap(source, new SingletonArrayFunc()).subscribeActual(s); + new FlowableMap<>(source, new SingletonArrayFunc()).subscribeActual(s); return; } - WithLatestFromSubscriber parent = new WithLatestFromSubscriber(s, combiner, n); + WithLatestFromSubscriber parent = new WithLatestFromSubscriber<>(s, combiner, n); s.onSubscribe(parent); parent.subscribe(others, n); @@ -123,8 +124,8 @@ static final class WithLatestFromSubscriber s[i] = new WithLatestInnerSubscriber(this, i); } this.subscribers = s; - this.values = new AtomicReferenceArray(n); - this.upstream = new AtomicReference(); + this.values = new AtomicReferenceArray<>(n); + this.upstream = new AtomicReference<>(); this.requested = new AtomicLong(); this.error = new AtomicThrowable(); } @@ -174,7 +175,7 @@ public boolean tryOnNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(combiner.apply(objects), "The combiner returned a null value"); + v = Objects.requireNonNull(combiner.apply(objects), "The combiner returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancel(); @@ -297,7 +298,7 @@ void dispose() { final class SingletonArrayFunc implements Function { @Override public R apply(T t) throws Throwable { - return ObjectHelper.requireNonNull(combiner.apply(new Object[] { t }), "The combiner returned a null value"); + return Objects.requireNonNull(combiner.apply(new Object[] { t }), "The combiner returned a null value"); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java index 09f1a59a7f..de680b19af 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZip.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.flowable; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -21,11 +22,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; public final class FlowableZip extends Flowable { @@ -71,7 +72,7 @@ public void subscribeActual(Subscriber s) { return; } - ZipCoordinator coordinator = new ZipCoordinator(s, zipper, count, bufferSize, delayError); + ZipCoordinator coordinator = new ZipCoordinator<>(s, zipper, count, bufferSize, delayError); s.onSubscribe(coordinator); @@ -108,7 +109,7 @@ static final class ZipCoordinator @SuppressWarnings("unchecked") ZipSubscriber[] a = new ZipSubscriber[n]; for (int i = 0; i < n; i++) { - a[i] = new ZipSubscriber(this, prefetch); + a[i] = new ZipSubscriber<>(this, prefetch); } this.current = new Object[n]; this.subscribers = a; @@ -191,23 +192,12 @@ void drain() { for (int j = 0; j < n; j++) { ZipSubscriber inner = qs[j]; if (values[j] == null) { + boolean d = inner.done; + SimpleQueue q = inner.queue; + T v = null; try { - boolean d = inner.done; - SimpleQueue q = inner.queue; - T v = q != null ? q.poll() : null; - - boolean sourceEmpty = v == null; - if (d && sourceEmpty) { - cancelAll(); - errors.tryTerminateConsumer(a); - return; - } - if (!sourceEmpty) { - values[j] = v; - } else { - empty = true; - } + v = q != null ? q.poll() : null; } catch (Throwable ex) { Exceptions.throwIfFatal(ex); @@ -217,6 +207,18 @@ void drain() { errors.tryTerminateConsumer(a); return; } + d = true; + } + + boolean sourceEmpty = v == null; + if (d && sourceEmpty) { + cancelAll(); + errors.tryTerminateConsumer(a); + return; + } + if (!sourceEmpty) { + values[j] = v; + } else { empty = true; } } @@ -229,7 +231,7 @@ void drain() { R v; try { - v = ObjectHelper.requireNonNull(zipper.apply(values.clone()), "The zipper returned a null value"); + v = Objects.requireNonNull(zipper.apply(values.clone()), "The zipper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancelAll(); @@ -259,20 +261,11 @@ void drain() { for (int j = 0; j < n; j++) { ZipSubscriber inner = qs[j]; if (values[j] == null) { + boolean d = inner.done; + SimpleQueue q = inner.queue; + T v = null; try { - boolean d = inner.done; - SimpleQueue q = inner.queue; - T v = q != null ? q.poll() : null; - - boolean empty = v == null; - if (d && empty) { - cancelAll(); - errors.tryTerminateConsumer(a); - return; - } - if (!empty) { - values[j] = v; - } + v = q != null ? q.poll() : null; } catch (Throwable ex) { Exceptions.throwIfFatal(ex); errors.tryAddThrowableOrReport(ex); @@ -281,6 +274,16 @@ void drain() { errors.tryTerminateConsumer(a); return; } + d = true; + } + boolean empty = v == null; + if (d && empty) { + cancelAll(); + errors.tryTerminateConsumer(a); + return; + } + if (!empty) { + values[j] = v; } } } @@ -354,7 +357,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); s.request(prefetch); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterable.java index fad3df6e8e..25d4df529d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.flowable; import java.util.Iterator; +import java.util.Objects; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -41,7 +41,7 @@ public void subscribeActual(Subscriber t) { Iterator it; try { - it = ObjectHelper.requireNonNull(other.iterator(), "The iterator returned by other is null"); + it = Objects.requireNonNull(other.iterator(), "The iterator returned by other is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptySubscription.error(e, t); @@ -99,17 +99,17 @@ public void onNext(T t) { U u; try { - u = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + u = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); } catch (Throwable e) { - error(e); + fail(e); return; } V v; try { - v = ObjectHelper.requireNonNull(zipper.apply(t, u), "The zipper function returned a null value"); + v = Objects.requireNonNull(zipper.apply(t, u), "The zipper function returned a null value"); } catch (Throwable e) { - error(e); + fail(e); return; } @@ -120,7 +120,7 @@ public void onNext(T t) { try { b = iterator.hasNext(); } catch (Throwable e) { - error(e); + fail(e); return; } @@ -131,7 +131,7 @@ public void onNext(T t) { } } - void error(Throwable e) { + void fail(Throwable e) { Exceptions.throwIfFatal(e); done = true; upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstream.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstream.java index ed559c96dc..348bb48227 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstream.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstream.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmb.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmb.java index b87390e847..802075b2ab 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmb.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmb.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCache.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCache.java index 64d57a0605..f9080a1282 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCache.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCache.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,13 +41,13 @@ public final class MaybeCache extends Maybe implements MaybeObserver { @SuppressWarnings("unchecked") public MaybeCache(MaybeSource source) { - this.source = new AtomicReference>(source); - this.observers = new AtomicReference[]>(EMPTY); + this.source = new AtomicReference<>(source); + this.observers = new AtomicReference<>(EMPTY); } @Override protected void subscribeActual(MaybeObserver observer) { - CacheDisposable parent = new CacheDisposable(observer, this); + CacheDisposable parent = new CacheDisposable<>(observer, this); observer.onSubscribe(parent); if (add(parent)) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserver.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserver.java index 774511f291..c94c7d4929 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArray.java index bab81330b2..889e229b64 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public MaybeConcatArray(MaybeSource[] sources) { @Override protected void subscribeActual(Subscriber s) { - ConcatMaybeObserver parent = new ConcatMaybeObserver(s, sources); + ConcatMaybeObserver parent = new ConcatMaybeObserver<>(s, sources); s.onSubscribe(parent); parent.drain(); } @@ -68,7 +68,7 @@ static final class ConcatMaybeObserver this.sources = sources; this.requested = new AtomicLong(); this.disposables = new SequentialDisposable(); - this.current = new AtomicReference(NotificationLite.COMPLETE); // as if a previous completed + this.current = new AtomicReference<>(NotificationLite.COMPLETE); // as if a previous completed } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayDelayError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayDelayError.java index a928db62a6..3b4b87399a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayDelayError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayDelayError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public MaybeConcatArrayDelayError(MaybeSource[] sources) { @Override protected void subscribeActual(Subscriber s) { - ConcatMaybeObserver parent = new ConcatMaybeObserver(s, sources); + ConcatMaybeObserver parent = new ConcatMaybeObserver<>(s, sources); s.onSubscribe(parent); parent.drain(); } @@ -71,7 +71,7 @@ static final class ConcatMaybeObserver this.sources = sources; this.requested = new AtomicLong(); this.disposables = new SequentialDisposable(); - this.current = new AtomicReference(NotificationLite.COMPLETE); // as if a previous completed + this.current = new AtomicReference<>(NotificationLite.COMPLETE); // as if a previous completed this.errors = new AtomicThrowable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterable.java index b01b58c6a0..b777f4c751 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,7 +23,6 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; @@ -45,14 +45,14 @@ protected void subscribeActual(Subscriber s) { Iterator> it; try { - it = ObjectHelper.requireNonNull(sources.iterator(), "The sources Iterable returned a null Iterator"); + it = Objects.requireNonNull(sources.iterator(), "The sources Iterable returned a null Iterator"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptySubscription.error(ex, s); return; } - ConcatMaybeObserver parent = new ConcatMaybeObserver(s, it); + ConcatMaybeObserver parent = new ConcatMaybeObserver<>(s, it); s.onSubscribe(parent); parent.drain(); } @@ -80,7 +80,7 @@ static final class ConcatMaybeObserver this.sources = sources; this.requested = new AtomicLong(); this.disposables = new SequentialDisposable(); - this.current = new AtomicReference(NotificationLite.COMPLETE); // as if a previous completed + this.current = new AtomicReference<>(NotificationLite.COMPLETE); // as if a previous completed } @Override @@ -169,7 +169,7 @@ void drain() { MaybeSource source; try { - source = ObjectHelper.requireNonNull(sources.next(), "The source Iterator returned a null MaybeSource"); + source = Objects.requireNonNull(sources.next(), "The source Iterator returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); a.onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContains.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContains.java index 55b68fec3d..c1154067f5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContains.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContains.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,8 +16,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.HasUpstreamMaybeSource; +import java.util.Objects; /** * Signals true if the source signals a value that is object-equals with the provided @@ -81,7 +81,7 @@ public void onSubscribe(Disposable d) { @Override public void onSuccess(Object value) { upstream = DisposableHelper.DISPOSED; - downstream.onSuccess(ObjectHelper.equals(value, this.value)); + downstream.onSuccess(Objects.equals(value, this.value)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCount.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCount.java index d687e6ac1e..cd2bececf1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCount.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCount.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreate.java index 2fc0f48414..1a6bc60d31 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public MaybeCreate(MaybeOnSubscribe source) { @Override protected void subscribeActual(MaybeObserver observer) { - Emitter parent = new Emitter(observer); + Emitter parent = new Emitter<>(observer); observer.onSubscribe(parent); try { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDefer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDefer.java index 3146b4781d..a1ffacef74 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDefer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDefer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; /** * Defers the creation of the actual Maybe the incoming MaybeObserver is subscribed to. @@ -37,7 +38,7 @@ protected void subscribeActual(MaybeObserver observer) { MaybeSource source; try { - source = ObjectHelper.requireNonNull(maybeSupplier.get(), "The maybeSupplier returned a null MaybeSource"); + source = Objects.requireNonNull(maybeSupplier.get(), "The maybeSupplier returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelay.java index 8509d90ab4..5f13010781 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,16 +33,19 @@ public final class MaybeDelay extends AbstractMaybeWithUpstream { final Scheduler scheduler; - public MaybeDelay(MaybeSource source, long delay, TimeUnit unit, Scheduler scheduler) { + final boolean delayError; + + public MaybeDelay(MaybeSource source, long delay, TimeUnit unit, Scheduler scheduler, boolean delayError) { super(source); this.delay = delay; this.unit = unit; this.scheduler = scheduler; + this.delayError = delayError; } @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new DelayMaybeObserver(observer, delay, unit, scheduler)); + source.subscribe(new DelayMaybeObserver<>(observer, delay, unit, scheduler, delayError)); } static final class DelayMaybeObserver @@ -59,15 +62,18 @@ static final class DelayMaybeObserver final Scheduler scheduler; + final boolean delayError; + T value; Throwable error; - DelayMaybeObserver(MaybeObserver actual, long delay, TimeUnit unit, Scheduler scheduler) { + DelayMaybeObserver(MaybeObserver actual, long delay, TimeUnit unit, Scheduler scheduler, boolean delayError) { this.downstream = actual; this.delay = delay; this.unit = unit; this.scheduler = scheduler; + this.delayError = delayError; } @Override @@ -105,21 +111,21 @@ public void onSubscribe(Disposable d) { @Override public void onSuccess(T value) { this.value = value; - schedule(); + schedule(delay); } @Override public void onError(Throwable e) { this.error = e; - schedule(); + schedule(delayError ? delay : 0); } @Override public void onComplete() { - schedule(); + schedule(delay); } - void schedule() { + void schedule(long delay) { DisposableHelper.replace(this, scheduler.scheduleDirect(this, delay, unit)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherPublisher.java index 0e3e8758fe..eebddc3c87 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public MaybeDelayOtherPublisher(MaybeSource source, Publisher other) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new DelayMaybeObserver(observer, other)); + source.subscribe(new DelayMaybeObserver<>(observer, other)); } static final class DelayMaybeObserver @@ -52,7 +52,7 @@ static final class DelayMaybeObserver Disposable upstream; DelayMaybeObserver(MaybeObserver actual, Publisher otherSource) { - this.other = new OtherSubscriber(actual); + this.other = new OtherSubscriber<>(actual); this.otherSource = otherSource; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionOtherPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionOtherPublisher.java index 09851e4af8..7cd54da714 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionOtherPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionOtherPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public MaybeDelaySubscriptionOtherPublisher(MaybeSource source, Publisher @Override protected void subscribeActual(MaybeObserver observer) { - other.subscribe(new OtherSubscriber(observer, source)); + other.subscribe(new OtherSubscriber<>(observer, source)); } static final class OtherSubscriber implements FlowableSubscriber, Disposable { @@ -51,7 +51,7 @@ static final class OtherSubscriber implements FlowableSubscriber, Dis Subscription upstream; OtherSubscriber(MaybeObserver actual, MaybeSource source) { - this.main = new DelayMaybeObserver(actual); + this.main = new DelayMaybeObserver<>(actual); this.source = source; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayWithCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayWithCompletable.java index 65a9460ec0..ecbd84a274 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayWithCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayWithCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public MaybeDelayWithCompletable(MaybeSource source, CompletableSource other) @Override protected void subscribeActual(MaybeObserver observer) { - other.subscribe(new OtherObserver(observer, source)); + other.subscribe(new OtherObserver<>(observer, source)); } static final class OtherObserver @@ -64,7 +64,7 @@ public void onError(Throwable e) { @Override public void onComplete() { - source.subscribe(new DelayWithMainObserver(this, downstream)); + source.subscribe(new DelayWithMainObserver<>(this, downstream)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDematerialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDematerialize.java new file mode 100644 index 0000000000..80b76d3171 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDematerialize.java @@ -0,0 +1,108 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +import java.util.Objects; + +/** + * Maps the success value of the source to a Notification, then + * maps it back to the corresponding signal type. + *

History: 2.2.4 - experimental + * @param the element type of the source + * @param the element type of the Notification and result + * @since 3.0.0 + */ +public final class MaybeDematerialize extends AbstractMaybeWithUpstream { + + final Function> selector; + + public MaybeDematerialize(Maybe source, Function> selector) { + super(source); + this.selector = selector; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new DematerializeObserver<>(observer, selector)); + } + + static final class DematerializeObserver implements MaybeObserver, Disposable { + + final MaybeObserver downstream; + + final Function> selector; + + Disposable upstream; + + DematerializeObserver(MaybeObserver downstream, + Function> selector) { + this.downstream = downstream; + this.selector = selector; + } + + @Override + public void dispose() { + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(upstream, d)) { + upstream = d; + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(T t) { + Notification notification; + + try { + notification = Objects.requireNonNull(selector.apply(t), "The selector returned a null Notification"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + if (notification.isOnNext()) { + downstream.onSuccess(notification.getValue()); + } else if (notification.isOnComplete()) { + downstream.onComplete(); + } else { + downstream.onError(notification.getError()); + } + } + + @Override + public void onError(Throwable e) { + downstream.onError(e); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetach.java index 9c4e4b9070..7dad3b0cf2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public MaybeDetach(MaybeSource source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new DetachMaybeObserver(observer)); + source.subscribe(new DetachMaybeObserver<>(observer)); } static final class DetachMaybeObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccess.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccess.java index 939f817ac9..c8f4799789 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccess.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccess.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public MaybeDoAfterSuccess(MaybeSource source, Consumer onAfterSuc @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new DoAfterObserver(observer, onAfterSuccess)); + source.subscribe(new DoAfterObserver<>(observer, onAfterSuccess)); } static final class DoAfterObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinally.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinally.java index eaeae0234f..0d8dfe8de2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinally.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinally.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public MaybeDoFinally(MaybeSource source, Action onFinally) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new DoFinallyObserver(observer, onFinally)); + source.subscribe(new DoFinallyObserver<>(observer, onFinally)); } static final class DoFinallyObserver extends AtomicInteger implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEvent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEvent.java index c70397473b..a2771dcb8c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEvent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEvent.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public MaybeDoOnEvent(MaybeSource source, BiConsumer observer) { - source.subscribe(new DoOnEventMaybeObserver(observer, onEvent)); + source.subscribe(new DoOnEventMaybeObserver<>(observer, onEvent)); } static final class DoOnEventMaybeObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnLifecycle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnLifecycle.java new file mode 100644 index 0000000000..0399329362 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnLifecycle.java @@ -0,0 +1,126 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Invokes callbacks upon {@code onSubscribe} from upstream and + * {@code dispose} from downstream. + * + * @param the element type of the flow + * @since 3.0.0 + */ +public final class MaybeDoOnLifecycle extends AbstractMaybeWithUpstream { + + final Consumer onSubscribe; + + final Action onDispose; + + public MaybeDoOnLifecycle(Maybe upstream, Consumer onSubscribe, + Action onDispose) { + super(upstream); + this.onSubscribe = onSubscribe; + this.onDispose = onDispose; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new MaybeLifecycleObserver<>(observer, onSubscribe, onDispose)); + } + + static final class MaybeLifecycleObserver implements MaybeObserver, Disposable { + + final MaybeObserver downstream; + + final Consumer onSubscribe; + + final Action onDispose; + + Disposable upstream; + + MaybeLifecycleObserver(MaybeObserver downstream, Consumer onSubscribe, Action onDispose) { + this.downstream = downstream; + this.onSubscribe = onSubscribe; + this.onDispose = onDispose; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + // this way, multiple calls to onSubscribe can show up in tests that use doOnSubscribe to validate behavior + try { + onSubscribe.accept(d); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + d.dispose(); + this.upstream = DisposableHelper.DISPOSED; + EmptyDisposable.error(e, downstream); + return; + } + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(@NonNull T t) { + if (upstream != DisposableHelper.DISPOSED) { + upstream = DisposableHelper.DISPOSED; + downstream.onSuccess(t); + } + } + + @Override + public void onError(@NonNull Throwable e) { + if (upstream != DisposableHelper.DISPOSED) { + upstream = DisposableHelper.DISPOSED; + downstream.onError(e); + } else { + RxJavaPlugins.onError(e); + } + } + + @Override + public void onComplete() { + if (upstream != DisposableHelper.DISPOSED) { + upstream = DisposableHelper.DISPOSED; + downstream.onComplete(); + } + } + + @Override + public void dispose() { + try { + onDispose.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + upstream.dispose(); + upstream = DisposableHelper.DISPOSED; + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminate.java index 7443b147a0..6c3494ac96 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmpty.java index 4b3361998e..a803b84fa9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.operators.ScalarSupplier; /** * Signals an onComplete. diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualSingle.java index e1e7e9fcbf..bf940b8223 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,7 +44,7 @@ public MaybeEqualSingle(MaybeSource source1, MaybeSource observer) { - EqualCoordinator parent = new EqualCoordinator(observer, isEqual); + EqualCoordinator parent = new EqualCoordinator<>(observer, isEqual); observer.onSubscribe(parent); parent.subscribe(source1, source2); } @@ -65,8 +65,8 @@ static final class EqualCoordinator super(2); this.downstream = actual; this.isEqual = isEqual; - this.observer1 = new EqualObserver(this); - this.observer2 = new EqualObserver(this); + this.observer1 = new EqualObserver<>(this); + this.observer2 = new EqualObserver<>(this); } void subscribe(MaybeSource source1, MaybeSource source2) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeError.java index a850a27b6d..ed9c391f02 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,7 +14,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; /** * Signals a constant Throwable. @@ -31,7 +31,7 @@ public MaybeError(Throwable error) { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.disposed()); + observer.onSubscribe(Disposable.disposed()); observer.onError(error); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorCallable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorCallable.java index f3b631ba36..360b729933 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorCallable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorCallable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,7 +14,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.util.ExceptionHelper; @@ -34,7 +34,7 @@ public MaybeErrorCallable(Supplier errorSupplier) { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.disposed()); + observer.onSubscribe(Disposable.disposed()); Throwable ex; try { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilter.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilter.java index a8c4f12176..833edb75f3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilter.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public MaybeFilter(MaybeSource source, Predicate predicate) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new FilterMaybeObserver(observer, predicate)); + source.subscribe(new FilterMaybeObserver<>(observer, predicate)); } static final class FilterMaybeObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingle.java index ff8e71b745..aa3cae5e5c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public MaybeFilterSingle(SingleSource source, Predicate predicate) @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new FilterMaybeObserver(observer, predicate)); + source.subscribe(new FilterMaybeObserver<>(observer, predicate)); } static final class FilterMaybeObserver implements SingleObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelector.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelector.java index c463567227..c57caee8f7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelector.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelector.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,12 +21,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps a source item to another MaybeSource then calls a BiFunction with the * original item and the secondary item to generate the final result. - * + * * @param the main value type * @param the second value type * @param the result value type @@ -59,7 +59,7 @@ static final class FlatMapBiMainObserver FlatMapBiMainObserver(MaybeObserver actual, Function> mapper, BiFunction resultSelector) { - this.inner = new InnerObserver(actual, resultSelector); + this.inner = new InnerObserver<>(actual, resultSelector); this.mapper = mapper; } @@ -85,7 +85,7 @@ public void onSuccess(T value) { MaybeSource next; try { - next = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); + next = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); inner.downstream.onError(ex); @@ -139,7 +139,7 @@ public void onSuccess(U value) { R r; try { - r = ObjectHelper.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value"); + r = Objects.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletable.java index 7ad1008421..1611fddfff 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps the success value of the source MaybeSource into a Completable. @@ -39,7 +39,7 @@ public MaybeFlatMapCompletable(MaybeSource source, Function parent = new FlatMapCompletableObserver(observer, mapper); + FlatMapCompletableObserver parent = new FlatMapCompletableObserver<>(observer, mapper); observer.onSubscribe(parent); source.subscribe(parent); } @@ -80,7 +80,7 @@ public void onSuccess(T value) { CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowable.java index 9c8aef2ba9..322a73177f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.AtomicLong; import org.reactivestreams.Subscriber; @@ -24,7 +25,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; @@ -48,7 +48,7 @@ public MaybeFlatMapIterableFlowable(MaybeSource source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new FlatMapIterableObserver(s, mapper)); + source.subscribe(new FlatMapIterableObserver<>(s, mapper)); } static final class FlatMapIterableObserver @@ -211,7 +211,7 @@ void drain() { R v; try { - v = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + v = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); a.onError(ex); @@ -279,11 +279,11 @@ public boolean isEmpty() { @Nullable @Override - public R poll() throws Exception { + public R poll() { Iterator iterator = it; if (iterator != null) { - R v = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + R v = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); if (!iterator.hasNext()) { it = null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservable.java index 8c2b4816c9..2018274b1a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; import java.util.Iterator; +import java.util.Objects; import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; @@ -21,7 +22,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicQueueDisposable; /** @@ -44,7 +44,7 @@ public MaybeFlatMapIterableObservable(MaybeSource source, @Override protected void subscribeActual(Observer observer) { - source.subscribe(new FlatMapIterableObserver(observer, mapper)); + source.subscribe(new FlatMapIterableObserver<>(observer, mapper)); } static final class FlatMapIterableObserver @@ -189,11 +189,11 @@ public boolean isEmpty() { @Nullable @Override - public R poll() throws Exception { + public R poll() { Iterator iterator = it; if (iterator != null) { - R v = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + R v = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); if (!iterator.hasNext()) { it = null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotification.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotification.java index 39007e5117..d91bd46da6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotification.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotification.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps a value into a MaybeSource and relays its signal. @@ -48,7 +48,7 @@ public MaybeFlatMapNotification(MaybeSource source, @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new FlatMapMaybeObserver(observer, onSuccessMapper, onErrorMapper, onCompleteSupplier)); + source.subscribe(new FlatMapMaybeObserver<>(observer, onSuccessMapper, onErrorMapper, onCompleteSupplier)); } static final class FlatMapMaybeObserver @@ -102,14 +102,16 @@ public void onSuccess(T value) { MaybeSource source; try { - source = ObjectHelper.requireNonNull(onSuccessMapper.apply(value), "The onSuccessMapper returned a null MaybeSource"); + source = Objects.requireNonNull(onSuccessMapper.apply(value), "The onSuccessMapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); return; } - source.subscribe(new InnerObserver()); + if (!isDisposed()) { + source.subscribe(new InnerObserver()); + } } @Override @@ -117,14 +119,16 @@ public void onError(Throwable e) { MaybeSource source; try { - source = ObjectHelper.requireNonNull(onErrorMapper.apply(e), "The onErrorMapper returned a null MaybeSource"); + source = Objects.requireNonNull(onErrorMapper.apply(e), "The onErrorMapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(new CompositeException(e, ex)); return; } - source.subscribe(new InnerObserver()); + if (!isDisposed()) { + source.subscribe(new InnerObserver()); + } } @Override @@ -132,14 +136,16 @@ public void onComplete() { MaybeSource source; try { - source = ObjectHelper.requireNonNull(onCompleteSupplier.get(), "The onCompleteSupplier returned a null MaybeSource"); + source = Objects.requireNonNull(onCompleteSupplier.get(), "The onCompleteSupplier returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); return; } - source.subscribe(new InnerObserver()); + if (!isDisposed()) { + source.subscribe(new InnerObserver()); + } } final class InnerObserver implements MaybeObserver { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingle.java index 011cbd9ab7..e88dd6fc3d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,7 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; -import java.util.NoSuchElementException; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -21,14 +21,15 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps the success value of the source MaybeSource into a Single. + *

History: 2.0.2 - experimental * @param the input value type * @param the result value type + * @since 2.1 */ -public final class MaybeFlatMapSingle extends Single { +public final class MaybeFlatMapSingle extends Maybe { final MaybeSource source; @@ -40,8 +41,8 @@ public MaybeFlatMapSingle(MaybeSource source, Function downstream) { - source.subscribe(new FlatMapMaybeObserver(downstream, mapper)); + protected void subscribeActual(MaybeObserver downstream) { + source.subscribe(new FlatMapMaybeObserver<>(downstream, mapper)); } static final class FlatMapMaybeObserver @@ -50,11 +51,11 @@ static final class FlatMapMaybeObserver private static final long serialVersionUID = 4827726964688405508L; - final SingleObserver downstream; + final MaybeObserver downstream; final Function> mapper; - FlatMapMaybeObserver(SingleObserver actual, Function> mapper) { + FlatMapMaybeObserver(MaybeObserver actual, Function> mapper) { this.downstream = actual; this.mapper = mapper; } @@ -81,7 +82,7 @@ public void onSuccess(T value) { SingleSource ss; try { - ss = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null SingleSource"); + ss = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); onError(ex); @@ -100,7 +101,7 @@ public void onError(Throwable e) { @Override public void onComplete() { - downstream.onError(new NoSuchElementException()); + downstream.onComplete(); } } @@ -108,9 +109,9 @@ static final class FlatMapSingleObserver implements SingleObserver { final AtomicReference parent; - final SingleObserver downstream; + final MaybeObserver downstream; - FlatMapSingleObserver(AtomicReference parent, SingleObserver downstream) { + FlatMapSingleObserver(AtomicReference parent, MaybeObserver downstream) { this.parent = parent; this.downstream = downstream; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElement.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElement.java deleted file mode 100644 index e823b06936..0000000000 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElement.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * 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. - */ - -package io.reactivex.rxjava3.internal.operators.maybe; - -import java.util.concurrent.atomic.AtomicReference; - -import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; - -/** - * Maps the success value of the source MaybeSource into a Single. - *

History: 2.0.2 - experimental - * @param the input value type - * @param the result value type - * @since 2.1 - */ -public final class MaybeFlatMapSingleElement extends Maybe { - - final MaybeSource source; - - final Function> mapper; - - public MaybeFlatMapSingleElement(MaybeSource source, Function> mapper) { - this.source = source; - this.mapper = mapper; - } - - @Override - protected void subscribeActual(MaybeObserver downstream) { - source.subscribe(new FlatMapMaybeObserver(downstream, mapper)); - } - - static final class FlatMapMaybeObserver - extends AtomicReference - implements MaybeObserver, Disposable { - - private static final long serialVersionUID = 4827726964688405508L; - - final MaybeObserver downstream; - - final Function> mapper; - - FlatMapMaybeObserver(MaybeObserver actual, Function> mapper) { - this.downstream = actual; - this.mapper = mapper; - } - - @Override - public void dispose() { - DisposableHelper.dispose(this); - } - - @Override - public boolean isDisposed() { - return DisposableHelper.isDisposed(get()); - } - - @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.setOnce(this, d)) { - downstream.onSubscribe(this); - } - } - - @Override - public void onSuccess(T value) { - SingleSource ss; - - try { - ss = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null SingleSource"); - } catch (Throwable ex) { - Exceptions.throwIfFatal(ex); - onError(ex); - return; - } - - ss.subscribe(new FlatMapSingleObserver(this, downstream)); - } - - @Override - public void onError(Throwable e) { - downstream.onError(e); - } - - @Override - public void onComplete() { - downstream.onComplete(); - } - } - - static final class FlatMapSingleObserver implements SingleObserver { - - final AtomicReference parent; - - final MaybeObserver downstream; - - FlatMapSingleObserver(AtomicReference parent, MaybeObserver downstream) { - this.parent = parent; - this.downstream = downstream; - } - - @Override - public void onSubscribe(final Disposable d) { - DisposableHelper.replace(parent, d); - } - - @Override - public void onSuccess(final R value) { - downstream.onSuccess(value); - } - - @Override - public void onError(final Throwable e) { - downstream.onError(e); - } - } -} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten.java index 837ba675ef..485cf69ea1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatten.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps a value into a MaybeSource and relays its signal. @@ -39,7 +39,7 @@ public MaybeFlatten(MaybeSource source, Function observer) { - source.subscribe(new FlatMapMaybeObserver(observer, mapper)); + source.subscribe(new FlatMapMaybeObserver<>(observer, mapper)); } static final class FlatMapMaybeObserver @@ -85,7 +85,7 @@ public void onSuccess(T value) { MaybeSource source; try { - source = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); + source = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromAction.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromAction.java index 8c29290deb..3d9cf428cf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromAction.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromAction.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public MaybeFromAction(Action action) { @Override protected void subscribeActual(MaybeObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (!d.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable.java index 4d218eb1ad..592b02fad2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public MaybeFromCallable(Callable callable) { @Override protected void subscribeActual(MaybeObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (!d.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletable.java index bdbc2ec03b..f6ad406e35 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ import io.reactivex.rxjava3.internal.fuseable.HasUpstreamCompletableSource; /** - * Wrap a Single into a Maybe. + * Wrap a Completable into a Maybe. * * @param the value type */ diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFuture.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFuture.java index db6c87518e..e506a32265 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFuture.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFuture.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public MaybeFromFuture(Future future, long timeout, TimeUnit unit) @Override protected void subscribeActual(MaybeObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (!d.isDisposed()) { T v; @@ -52,6 +52,7 @@ protected void subscribeActual(MaybeObserver observer) { v = future.get(timeout, unit); } } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); if (ex instanceof ExecutionException) { ex = ex.getCause(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnable.java index 672bd4edf5..a102e4b945 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public MaybeFromRunnable(Runnable runnable) { @Override protected void subscribeActual(MaybeObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (!d.isDisposed()) { @@ -58,7 +58,7 @@ protected void subscribeActual(MaybeObserver observer) { } @Override - public T get() throws Throwable { + public T get() { runnable.run(); return null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingle.java index 7ea38ea986..8fe691b767 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public SingleSource source() { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new FromSingleObserver(observer)); + source.subscribe(new FromSingleObserver<>(observer)); } static final class FromSingleObserver implements SingleObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplier.java index 52aba416d9..6eaf31ee64 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public MaybeFromSupplier(Supplier supplier) { @Override protected void subscribeActual(MaybeObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (!d.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHide.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHide.java index 2950734c37..2f87b5fddd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHide.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHide.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public MaybeHide(MaybeSource source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new HideMaybeObserver(observer)); + source.subscribe(new HideMaybeObserver<>(observer)); } static final class HideMaybeObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElement.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElement.java index 8750bc4f7e..dcdbd2cd73 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElement.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElement.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public MaybeIgnoreElement(MaybeSource source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new IgnoreMaybeObserver(observer)); + source.subscribe(new IgnoreMaybeObserver<>(observer)); } static final class IgnoreMaybeObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementCompletable.java index 4b2ea8d462..42f40c92f1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,12 +34,12 @@ public MaybeIgnoreElementCompletable(MaybeSource source) { @Override protected void subscribeActual(CompletableObserver observer) { - source.subscribe(new IgnoreMaybeObserver(observer)); + source.subscribe(new IgnoreMaybeObserver<>(observer)); } @Override public Maybe fuseToMaybe() { - return RxJavaPlugins.onAssembly(new MaybeIgnoreElement(source)); + return RxJavaPlugins.onAssembly(new MaybeIgnoreElement<>(source)); } static final class IgnoreMaybeObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmpty.java index 83ec1d278a..a9642b92fe 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public MaybeIsEmpty(MaybeSource source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new IsEmptyMaybeObserver(observer)); + source.subscribe(new IsEmptyMaybeObserver<>(observer)); } static final class IsEmptyMaybeObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingle.java index c2f3bf4428..b4795a2985 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,12 +41,12 @@ public MaybeSource source() { @Override public Maybe fuseToMaybe() { - return RxJavaPlugins.onAssembly(new MaybeIsEmpty(source)); + return RxJavaPlugins.onAssembly(new MaybeIsEmpty<>(source)); } @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new IsEmptyMaybeObserver(observer)); + source.subscribe(new IsEmptyMaybeObserver<>(observer)); } static final class IsEmptyMaybeObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJust.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJust.java index a2c70740a6..7e6fc6dfa6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJust.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJust.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,8 +14,8 @@ package io.reactivex.rxjava3.internal.operators.maybe; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.operators.ScalarSupplier; /** * Signals a constant value. @@ -32,7 +32,7 @@ public MaybeJust(T value) { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.disposed()); + observer.onSubscribe(Disposable.disposed()); observer.onSuccess(value); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeLift.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeLift.java index 38cb73972c..a5a6d76877 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeLift.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeLift.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; /** * Calls a MaybeOperator for the incoming MaybeObserver. @@ -38,7 +39,7 @@ protected void subscribeActual(MaybeObserver observer) { MaybeObserver lifted; try { - lifted = ObjectHelper.requireNonNull(operator.apply(observer), "The operator returned a null MaybeObserver"); + lifted = Objects.requireNonNull(operator.apply(observer), "The operator returned a null MaybeObserver"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMap.java index c2a14f994d..2d637fdd43 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; /** * Maps the upstream success value into some other value. @@ -79,7 +80,7 @@ public void onSuccess(T value) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null item"); + v = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null item"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterialize.java index 02e6691578..022d92bbe8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,6 +34,6 @@ public MaybeMaterialize(Maybe source) { @Override protected void subscribeActual(SingleObserver> observer) { - source.subscribe(new MaterializeSingleObserver(observer)); + source.subscribe(new MaterializeSingleObserver<>(observer)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArray.java index d5e5069d22..82f6d62ddf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.*; @@ -21,10 +22,9 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Run all MaybeSources of an array at once and signal their values as they become available. @@ -47,11 +47,11 @@ protected void subscribeActual(Subscriber s) { SimpleQueueWithConsumerIndex queue; if (n <= bufferSize()) { - queue = new MpscFillOnceSimpleQueue(n); + queue = new MpscFillOnceSimpleQueue<>(n); } else { - queue = new ClqSimpleQueue(); + queue = new ClqSimpleQueue<>(); } - MergeMaybeObserver parent = new MergeMaybeObserver(s, n, queue); + MergeMaybeObserver parent = new MergeMaybeObserver<>(s, n, queue); s.onSubscribe(parent); @@ -110,7 +110,7 @@ public int requestFusion(int mode) { @Nullable @SuppressWarnings("unchecked") @Override - public T poll() throws Exception { + public T poll() { for (;;) { Object o = queue.poll(); if (o != NotificationLite.COMPLETE) { @@ -327,7 +327,7 @@ static final class MpscFillOnceSimpleQueue @Override public boolean offer(T value) { - ObjectHelper.requireNonNull(value, "value is null"); + Objects.requireNonNull(value, "value is null"); int idx = producerIndex.getAndIncrement(); if (idx < length()) { lazySet(idx, value); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeNever.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeNever.java index 9690c28359..a9c35c9b67 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeNever.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeNever.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn.java index 0bf89494e9..5afb4beaf3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeObserveOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public MaybeObserveOn(MaybeSource source, Scheduler scheduler) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new ObserveOnMaybeObserver(observer, scheduler)); + source.subscribe(new ObserveOnMaybeObserver<>(observer, scheduler)); } static final class ObserveOnMaybeObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorComplete.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorComplete.java index cd82570f58..46c15bcb2a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorComplete.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorComplete.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,10 +37,11 @@ public MaybeOnErrorComplete(MaybeSource source, @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new OnErrorCompleteMaybeObserver(observer, predicate)); + source.subscribe(new OnErrorCompleteMultiObserver<>(observer, predicate)); } - static final class OnErrorCompleteMaybeObserver implements MaybeObserver, Disposable { + public static final class OnErrorCompleteMultiObserver + implements MaybeObserver, SingleObserver, Disposable { final MaybeObserver downstream; @@ -48,7 +49,7 @@ static final class OnErrorCompleteMaybeObserver implements MaybeObserver, Disposable upstream; - OnErrorCompleteMaybeObserver(MaybeObserver actual, Predicate predicate) { + public OnErrorCompleteMultiObserver(MaybeObserver actual, Predicate predicate) { this.downstream = actual; this.predicate = predicate; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorNext.java index 15ec49c3a9..a3e975570d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,30 +21,25 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Subscribes to the MaybeSource returned by a function if the main source signals an onError. - * + * * @param the value type */ public final class MaybeOnErrorNext extends AbstractMaybeWithUpstream { final Function> resumeFunction; - final boolean allowFatal; - public MaybeOnErrorNext(MaybeSource source, - Function> resumeFunction, - boolean allowFatal) { + Function> resumeFunction) { super(source); this.resumeFunction = resumeFunction; - this.allowFatal = allowFatal; } @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new OnErrorNextMaybeObserver(observer, resumeFunction, allowFatal)); + source.subscribe(new OnErrorNextMaybeObserver<>(observer, resumeFunction)); } static final class OnErrorNextMaybeObserver @@ -56,14 +52,10 @@ static final class OnErrorNextMaybeObserver final Function> resumeFunction; - final boolean allowFatal; - OnErrorNextMaybeObserver(MaybeObserver actual, - Function> resumeFunction, - boolean allowFatal) { + Function> resumeFunction) { this.downstream = actual; this.resumeFunction = resumeFunction; - this.allowFatal = allowFatal; } @Override @@ -90,14 +82,10 @@ public void onSuccess(T value) { @Override public void onError(Throwable e) { - if (!allowFatal && !(e instanceof Exception)) { - downstream.onError(e); - return; - } MaybeSource m; try { - m = ObjectHelper.requireNonNull(resumeFunction.apply(e), "The resumeFunction returned a null MaybeSource"); + m = Objects.requireNonNull(resumeFunction.apply(e), "The resumeFunction returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(new CompositeException(e, ex)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorReturn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorReturn.java index 5c61179678..6d14b2c8cd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorReturn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorReturn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,8 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; /** * Returns a value generated via a function if the main source signals an onError. @@ -26,31 +27,31 @@ */ public final class MaybeOnErrorReturn extends AbstractMaybeWithUpstream { - final Function valueSupplier; + final Function itemSupplier; public MaybeOnErrorReturn(MaybeSource source, - Function valueSupplier) { + Function itemSupplier) { super(source); - this.valueSupplier = valueSupplier; + this.itemSupplier = itemSupplier; } @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new OnErrorReturnMaybeObserver(observer, valueSupplier)); + source.subscribe(new OnErrorReturnMaybeObserver<>(observer, itemSupplier)); } static final class OnErrorReturnMaybeObserver implements MaybeObserver, Disposable { final MaybeObserver downstream; - final Function valueSupplier; + final Function itemSupplier; Disposable upstream; OnErrorReturnMaybeObserver(MaybeObserver actual, Function valueSupplier) { this.downstream = actual; - this.valueSupplier = valueSupplier; + this.itemSupplier = valueSupplier; } @Override @@ -82,7 +83,7 @@ public void onError(Throwable e) { T v; try { - v = ObjectHelper.requireNonNull(valueSupplier.apply(e), "The valueSupplier returned a null value"); + v = Objects.requireNonNull(itemSupplier.apply(e), "The itemSupplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(new CompositeException(e, ex)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeek.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeek.java index 1b4b717367..58c69839a1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeek.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeek.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,7 +53,7 @@ public MaybePeek(MaybeSource source, Consumer onSubscribe @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new MaybePeekObserver(observer, this)); + source.subscribe(new MaybePeekObserver<>(observer, this)); } static final class MaybePeekObserver implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOn.java index 8a934d5216..7ec1a6ad71 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,10 +34,10 @@ public MaybeSubscribeOn(MaybeSource source, Scheduler scheduler) { @Override protected void subscribeActual(MaybeObserver observer) { - SubscribeOnMaybeObserver parent = new SubscribeOnMaybeObserver(observer); + SubscribeOnMaybeObserver parent = new SubscribeOnMaybeObserver<>(observer); observer.onSubscribe(parent); - parent.task.replace(scheduler.scheduleDirect(new SubscribeTask(parent, source))); + parent.task.replace(scheduler.scheduleDirect(new SubscribeTask<>(parent, source))); } static final class SubscribeTask implements Runnable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmpty.java index e1b218078e..c432b3c674 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public MaybeSwitchIfEmpty(MaybeSource source, MaybeSource other) @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new SwitchIfEmptyMaybeObserver(observer, other)); + source.subscribe(new SwitchIfEmptyMaybeObserver<>(observer, other)); } static final class SwitchIfEmptyMaybeObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingle.java index 3533092a14..29e77a3e56 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ public MaybeSource source() { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new SwitchIfEmptyMaybeObserver(observer, other)); + source.subscribe(new SwitchIfEmptyMaybeObserver<>(observer, other)); } static final class SwitchIfEmptyMaybeObserver @@ -124,4 +124,4 @@ public void onError(Throwable e) { } } -} \ No newline at end of file +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilMaybe.java index 9b53acf077..e99011e509 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public MaybeTakeUntilMaybe(MaybeSource source, MaybeSource other) { @Override protected void subscribeActual(MaybeObserver observer) { - TakeUntilMainMaybeObserver parent = new TakeUntilMainMaybeObserver(observer); + TakeUntilMainMaybeObserver parent = new TakeUntilMainMaybeObserver<>(observer); observer.onSubscribe(parent); other.subscribe(parent.other); @@ -57,7 +57,7 @@ static final class TakeUntilMainMaybeObserver TakeUntilMainMaybeObserver(MaybeObserver downstream) { this.downstream = downstream; - this.other = new TakeUntilOtherMaybeObserver(this); + this.other = new TakeUntilOtherMaybeObserver<>(this); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisher.java index 0ef77d40e2..8e57a5ee84 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public MaybeTakeUntilPublisher(MaybeSource source, Publisher other) { @Override protected void subscribeActual(MaybeObserver observer) { - TakeUntilMainMaybeObserver parent = new TakeUntilMainMaybeObserver(observer); + TakeUntilMainMaybeObserver parent = new TakeUntilMainMaybeObserver<>(observer); observer.onSubscribe(parent); other.subscribe(parent.other); @@ -60,7 +60,7 @@ static final class TakeUntilMainMaybeObserver TakeUntilMainMaybeObserver(MaybeObserver downstream) { this.downstream = downstream; - this.other = new TakeUntilOtherMaybeObserver(this); + this.other = new TakeUntilOtherMaybeObserver<>(this); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeInterval.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeInterval.java new file mode 100644 index 0000000000..9580976d89 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeInterval.java @@ -0,0 +1,105 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.schedulers.Timed; + +/** + * Measures the time between subscription and the success item emission + * from the upstream and emits this as a {@link Timed} success value. + * @param the element type of the sequence + * @since 3.0.0 + */ +public final class MaybeTimeInterval extends Maybe> { + + final MaybeSource source; + + final TimeUnit unit; + + final Scheduler scheduler; + + final boolean start; + + public MaybeTimeInterval(MaybeSource source, TimeUnit unit, Scheduler scheduler, boolean start) { + this.source = source; + this.unit = unit; + this.scheduler = scheduler; + this.start = start; + } + + @Override + protected void subscribeActual(@NonNull MaybeObserver> observer) { + source.subscribe(new TimeIntervalMaybeObserver<>(observer, unit, scheduler, start)); + } + + static final class TimeIntervalMaybeObserver implements MaybeObserver, Disposable { + + final MaybeObserver> downstream; + + final TimeUnit unit; + + final Scheduler scheduler; + + final long startTime; + + Disposable upstream; + + TimeIntervalMaybeObserver(MaybeObserver> downstream, TimeUnit unit, Scheduler scheduler, boolean start) { + this.downstream = downstream; + this.unit = unit; + this.scheduler = scheduler; + this.startTime = start ? scheduler.now(unit) : 0L; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(@NonNull T t) { + downstream.onSuccess(new Timed<>(t, scheduler.now(unit) - startTime, unit)); + } + + @Override + public void onError(@NonNull Throwable e) { + downstream.onError(e); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public void dispose() { + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutMaybe.java index aee0b524c2..2a2581c5f3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ public MaybeTimeoutMaybe(MaybeSource source, MaybeSource other, MaybeSourc @Override protected void subscribeActual(MaybeObserver observer) { - TimeoutMainMaybeObserver parent = new TimeoutMainMaybeObserver(observer, fallback); + TimeoutMainMaybeObserver parent = new TimeoutMainMaybeObserver<>(observer, fallback); observer.onSubscribe(parent); other.subscribe(parent.other); @@ -66,9 +66,9 @@ static final class TimeoutMainMaybeObserver TimeoutMainMaybeObserver(MaybeObserver actual, MaybeSource fallback) { this.downstream = actual; - this.other = new TimeoutOtherMaybeObserver(this); + this.other = new TimeoutOtherMaybeObserver<>(this); this.fallback = fallback; - this.otherObserver = fallback != null ? new TimeoutFallbackMaybeObserver(actual) : null; + this.otherObserver = fallback != null ? new TimeoutFallbackMaybeObserver<>(actual) : null; } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisher.java index ae1a2efd18..e89a8d0bc9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ public MaybeTimeoutPublisher(MaybeSource source, Publisher other, MaybeSou @Override protected void subscribeActual(MaybeObserver observer) { - TimeoutMainMaybeObserver parent = new TimeoutMainMaybeObserver(observer, fallback); + TimeoutMainMaybeObserver parent = new TimeoutMainMaybeObserver<>(observer, fallback); observer.onSubscribe(parent); other.subscribe(parent.other); @@ -69,9 +69,9 @@ static final class TimeoutMainMaybeObserver TimeoutMainMaybeObserver(MaybeObserver actual, MaybeSource fallback) { this.downstream = actual; - this.other = new TimeoutOtherMaybeObserver(this); + this.other = new TimeoutOtherMaybeObserver<>(this); this.fallback = fallback; - this.otherObserver = fallback != null ? new TimeoutFallbackMaybeObserver(actual) : null; + this.otherObserver = fallback != null ? new TimeoutFallbackMaybeObserver<>(actual) : null; } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimer.java index a374c44ced..2b9033c66b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowable.java index 11b713265d..5ce9f66103 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ public MaybeSource source() { @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new MaybeToFlowableSubscriber(s)); + source.subscribe(new MaybeToFlowableSubscriber<>(s)); } static final class MaybeToFlowableSubscriber extends DeferredScalarSubscription diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservable.java index 75129b3514..65ef0ced1b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -52,7 +52,7 @@ protected void subscribeActual(Observer observer) { * @since 2.2 */ public static MaybeObserver create(Observer downstream) { - return new MaybeToObservableObserver(downstream); + return new MaybeToObservableObserver<>(downstream); } static final class MaybeToObservableObserver extends DeferredScalarDisposable diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToPublisher.java index b6552e9726..8f1f10d28d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public static Function, Publisher> instance() { } @Override - public Publisher apply(MaybeSource t) throws Exception { - return new MaybeToFlowable(t); + public Publisher apply(MaybeSource t) { + return new MaybeToFlowable<>(t); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingle.java index c620cef68e..48ab0134e3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ /** * Wraps a MaybeSource and exposes its onSuccess and onError signals and signals - * NoSuchElementException for onComplete. + * NoSuchElementException for onComplete if {@code defaultValue} is null. * * @param the value type */ @@ -43,7 +43,7 @@ public MaybeSource source() { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new ToSingleMaybeSubscriber(observer, defaultValue)); + source.subscribe(new ToSingleMaybeSubscriber<>(observer, defaultValue)); } static final class ToSingleMaybeSubscriber implements MaybeObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsafeCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsafeCreate.java index 35621c1bbf..462a2525b1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsafeCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsafeCreate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOn.java index 6e29ffa610..b1d8cb6f65 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public MaybeUnsubscribeOn(MaybeSource source, Scheduler scheduler) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new UnsubscribeOnMaybeObserver(observer, scheduler)); + source.subscribe(new UnsubscribeOnMaybeObserver<>(observer, scheduler)); } static final class UnsubscribeOnMaybeObserver extends AtomicReference diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsing.java index 628c4dda23..74e4250e5f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsing.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -65,7 +65,7 @@ protected void subscribeActual(MaybeObserver observer) { MaybeSource source; try { - source = ObjectHelper.requireNonNull(sourceSupplier.apply(resource), "The sourceSupplier returned a null MaybeSource"); + source = Objects.requireNonNull(sourceSupplier.apply(resource), "The sourceSupplier returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (eager) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArray.java index 9d83976966..ac7514fb68 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.maybe; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class MaybeZipArray extends Maybe { @@ -40,11 +40,11 @@ protected void subscribeActual(MaybeObserver observer) { int n = sources.length; if (n == 1) { - sources[0].subscribe(new MaybeMap.MapMaybeObserver(observer, new SingletonArrayFunc())); + sources[0].subscribe(new MaybeMap.MapMaybeObserver<>(observer, new SingletonArrayFunc())); return; } - ZipCoordinator parent = new ZipCoordinator(observer, n, zipper); + ZipCoordinator parent = new ZipCoordinator<>(observer, n, zipper); observer.onSubscribe(parent); @@ -73,7 +73,7 @@ static final class ZipCoordinator extends AtomicInteger implements Disposa final ZipMaybeObserver[] observers; - final Object[] values; + Object[] values; @SuppressWarnings("unchecked") ZipCoordinator(MaybeObserver observer, int n, Function zipper) { @@ -82,7 +82,7 @@ static final class ZipCoordinator extends AtomicInteger implements Disposa this.zipper = zipper; ZipMaybeObserver[] o = new ZipMaybeObserver[n]; for (int i = 0; i < n; i++) { - o[i] = new ZipMaybeObserver(this, i); + o[i] = new ZipMaybeObserver<>(this, i); } this.observers = o; this.values = new Object[n]; @@ -99,22 +99,29 @@ public void dispose() { for (ZipMaybeObserver d : observers) { d.dispose(); } + + values = null; } } void innerSuccess(T value, int index) { - values[index] = value; + Object[] values = this.values; + if (values != null) { + values[index] = value; + } if (decrementAndGet() == 0) { R v; try { - v = ObjectHelper.requireNonNull(zipper.apply(values), "The zipper returned a null value"); + v = Objects.requireNonNull(zipper.apply(values), "The zipper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); + this.values = null; downstream.onError(ex); return; } + this.values = null; downstream.onSuccess(v); } } @@ -133,6 +140,7 @@ void disposeExcept(int index) { void innerError(Throwable ex, int index) { if (getAndSet(0) > 0) { disposeExcept(index); + values = null; downstream.onError(ex); } else { RxJavaPlugins.onError(ex); @@ -142,6 +150,7 @@ void innerError(Throwable ex, int index) { void innerComplete(int index) { if (getAndSet(0) > 0) { disposeExcept(index); + values = null; downstream.onComplete(); } } @@ -190,7 +199,7 @@ public void onComplete() { final class SingletonArrayFunc implements Function { @Override public R apply(T t) throws Throwable { - return ObjectHelper.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); + return Objects.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterable.java index efcd10b136..b247ca4ded 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,12 +14,12 @@ package io.reactivex.rxjava3.internal.operators.maybe; import java.util.Arrays; +import java.util.Objects; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.maybe.MaybeZipArray.ZipCoordinator; public final class MaybeZipIterable extends Maybe { @@ -62,11 +62,11 @@ protected void subscribeActual(MaybeObserver observer) { } if (n == 1) { - a[0].subscribe(new MaybeMap.MapMaybeObserver(observer, new SingletonArrayFunc())); + a[0].subscribe(new MaybeMap.MapMaybeObserver<>(observer, new SingletonArrayFunc())); return; } - ZipCoordinator parent = new ZipCoordinator(observer, n, zipper); + ZipCoordinator parent = new ZipCoordinator<>(observer, n, zipper); observer.onSubscribe(parent); @@ -82,7 +82,7 @@ protected void subscribeActual(MaybeObserver observer) { final class SingletonArrayFunc implements Function { @Override public R apply(T t) throws Throwable { - return ObjectHelper.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); + return Objects.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservable.java index 28bdae282a..9f46c097f6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public CompletableAndThenObservable(CompletableSource source, @Override protected void subscribeActual(Observer observer) { - AndThenObservableObserver parent = new AndThenObservableObserver(observer, other); + AndThenObservableObserver parent = new AndThenObservableObserver<>(observer, other); observer.onSubscribe(parent); source.subscribe(parent); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisher.java index c9017d0af6..7004181bcc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java new file mode 100644 index 0000000000..d2d29b1213 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainObserver.java @@ -0,0 +1,152 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; + +/** + * Base class for implementing concatMapX main observers. + * + * @param the upstream value type + * @since 3.0.10 + */ +public abstract class ConcatMapXMainObserver extends AtomicInteger +implements Observer, Disposable { + + private static final long serialVersionUID = -3214213361171757852L; + + final AtomicThrowable errors; + + final int prefetch; + + final ErrorMode errorMode; + + SimpleQueue queue; + + Disposable upstream; + + volatile boolean done; + + volatile boolean disposed; + + public ConcatMapXMainObserver(int prefetch, ErrorMode errorMode) { + this.errorMode = errorMode; + this.errors = new AtomicThrowable(); + this.prefetch = prefetch; + } + + @Override + public final void onSubscribe(Disposable d) { + if (DisposableHelper.validate(upstream, d)) { + upstream = d; + if (d instanceof QueueDisposable) { + @SuppressWarnings("unchecked") + QueueDisposable qd = (QueueDisposable)d; + int mode = qd.requestFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + if (mode == QueueFuseable.SYNC) { + queue = qd; + done = true; + + onSubscribeDownstream(); + + drain(); + return; + } + else if (mode == QueueFuseable.ASYNC) { + queue = qd; + + onSubscribeDownstream(); + + return; + } + } + + queue = new SpscLinkedArrayQueue<>(prefetch); + onSubscribeDownstream(); + } + } + + @Override + public final void onNext(T t) { + // In async fusion mode, t is a drain indicator + if (t != null) { + queue.offer(t); + } + drain(); + } + + @Override + public final void onError(Throwable t) { + if (errors.tryAddThrowableOrReport(t)) { + if (errorMode == ErrorMode.IMMEDIATE) { + disposeInner(); + } + done = true; + drain(); + } + } + + @Override + public final void onComplete() { + done = true; + drain(); + } + + @Override + public final void dispose() { + disposed = true; + upstream.dispose(); + disposeInner(); + errors.tryTerminateAndReport(); + if (getAndIncrement() == 0) { + queue.clear(); + clearValue(); + } + } + + @Override + public final boolean isDisposed() { + return disposed; + } + + /** + * Override this to clear values when the downstream disposes. + */ + void clearValue() { + } + + /** + * Typically, this should be {@code downstream.onSubscribe(this)}. + */ + abstract void onSubscribeDownstream(); + + /** + * Typically, this should be {@code inner.dispose()}. + */ + abstract void disposeInner(); + + /** + * Implement the serialized inner subscribing and value emission here. + */ + abstract void drain(); +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java new file mode 100644 index 0000000000..03e4c568d6 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ConcatMapXMainSubscriber.java @@ -0,0 +1,157 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; + +/** + * Base class for implementing concatMapX main subscribers. + * + * @param the upstream value type + * @since 3.0.10 + */ +public abstract class ConcatMapXMainSubscriber extends AtomicInteger +implements FlowableSubscriber { + + private static final long serialVersionUID = -3214213361171757852L; + + final AtomicThrowable errors; + + final int prefetch; + + final ErrorMode errorMode; + + SimpleQueue queue; + + Subscription upstream; + + volatile boolean done; + + volatile boolean cancelled; + + boolean syncFused; + + public ConcatMapXMainSubscriber(int prefetch, ErrorMode errorMode) { + this.errorMode = errorMode; + this.errors = new AtomicThrowable(); + this.prefetch = prefetch; + } + + @Override + public final void onSubscribe(Subscription s) { + if (SubscriptionHelper.validate(upstream, s)) { + upstream = s; + if (s instanceof QueueSubscription) { + @SuppressWarnings("unchecked") + QueueSubscription qs = (QueueSubscription)s; + int mode = qs.requestFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + if (mode == QueueFuseable.SYNC) { + queue = qs; + syncFused = true; + done = true; + + onSubscribeDownstream(); + + drain(); + return; + } + else if (mode == QueueFuseable.ASYNC) { + queue = qs; + + onSubscribeDownstream(); + + upstream.request(prefetch); + return; + } + } + + queue = new SpscArrayQueue<>(prefetch); + onSubscribeDownstream(); + upstream.request(prefetch); + } + } + + @Override + public final void onNext(T t) { + // In async fusion mode, t is a drain indicator + if (t != null) { + if (!queue.offer(t)) { + upstream.cancel(); + onError(new QueueOverflowException()); + return; + } + } + drain(); + } + + @Override + public final void onError(Throwable t) { + if (errors.tryAddThrowableOrReport(t)) { + if (errorMode == ErrorMode.IMMEDIATE) { + disposeInner(); + } + done = true; + drain(); + } + } + + @Override + public final void onComplete() { + done = true; + drain(); + } + + final void stop() { + cancelled = true; + upstream.cancel(); + disposeInner(); + errors.tryTerminateAndReport(); + if (getAndIncrement() == 0) { + queue.clear(); + clearValue(); + } + } + + /** + * Override this to clear values when the downstream disposes. + */ + void clearValue() { + } + + /** + * Typically, this should be {@code downstream.onSubscribe(this);}. + */ + abstract void onSubscribeDownstream(); + + /** + * Typically, this should be {@code inner.dispose()}. + */ + abstract void disposeInner(); + + /** + * Implement the serialized inner subscribing and value emission here. + */ + abstract void drain(); +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java index 6e16c9b6a9..ab93e4c27f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,20 +13,16 @@ package io.reactivex.rxjava3.internal.operators.mixed; -import java.util.concurrent.atomic.*; - -import org.reactivestreams.Subscription; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Maps the upstream items into {@link CompletableSource}s and subscribes to them one after the @@ -57,12 +53,12 @@ public FlowableConcatMapCompletable(Flowable source, @Override protected void subscribeActual(CompletableObserver observer) { - source.subscribe(new ConcatMapCompletableObserver(observer, mapper, errorMode, prefetch)); + source.subscribe(new ConcatMapCompletableObserver<>(observer, mapper, errorMode, prefetch)); } static final class ConcatMapCompletableObserver - extends AtomicInteger - implements FlowableSubscriber, Disposable { + extends ConcatMapXMainSubscriber + implements Disposable { private static final long serialVersionUID = 3610901111000061034L; @@ -70,93 +66,39 @@ static final class ConcatMapCompletableObserver final Function mapper; - final ErrorMode errorMode; - - final AtomicThrowable errors; - final ConcatMapInnerObserver inner; - final int prefetch; - - final SimplePlainQueue queue; - - Subscription upstream; - volatile boolean active; - volatile boolean done; - - volatile boolean disposed; - int consumed; ConcatMapCompletableObserver(CompletableObserver downstream, Function mapper, ErrorMode errorMode, int prefetch) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.prefetch = prefetch; - this.errors = new AtomicThrowable(); this.inner = new ConcatMapInnerObserver(this); - this.queue = new SpscArrayQueue(prefetch); - } - - @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(upstream, s)) { - this.upstream = s; - downstream.onSubscribe(this); - s.request(prefetch); - } - } - - @Override - public void onNext(T t) { - if (queue.offer(t)) { - drain(); - } else { - upstream.cancel(); - onError(new MissingBackpressureException("Queue full?!")); - } } @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - errors.tryTerminateConsumer(downstream); - if (getAndIncrement() == 0) { - queue.clear(); - } - } else { - done = true; - drain(); - } - } + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void onComplete() { - done = true; - drain(); + void disposeInner() { + inner.dispose(); } @Override public void dispose() { - disposed = true; - upstream.cancel(); - inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - } + stop(); } @Override public boolean isDisposed() { - return disposed; + return cancelled; } void innerError(Throwable ex) { @@ -179,29 +121,45 @@ void innerComplete() { drain(); } + @Override void drain() { if (getAndIncrement() != 0) { return; } + ErrorMode errorMode = this.errorMode; + SimpleQueue queue = this.queue; + AtomicThrowable errors = this.errors; + boolean syncFused = this.syncFused; + do { - if (disposed) { + if (cancelled) { queue.clear(); return; } - if (!active) { - - if (errorMode == ErrorMode.BOUNDARY) { - if (errors.get() != null) { - queue.clear(); - errors.tryTerminateConsumer(downstream); - return; - } + if (errors.get() != null) { + if (errorMode == ErrorMode.IMMEDIATE + || (errorMode == ErrorMode.BOUNDARY && !active)) { + queue.clear(); + errors.tryTerminateConsumer(downstream); + return; } + } + + if (!active) { boolean d = done; - T v = queue.poll(); + T v; + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -212,18 +170,21 @@ void drain() { if (!empty) { int limit = prefetch - (prefetch >> 1); - int c = consumed + 1; - if (c == limit) { - consumed = 0; - upstream.request(limit); - } else { - consumed = c; + + if (!syncFused) { + int c = consumed + 1; + if (c == limit) { + consumed = 0; + upstream.request(limit); + } else { + consumed = c; + } } CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); queue.clear(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java index 39fa04b174..944ac69311 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,20 +13,18 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Maps each upstream item into a {@link MaybeSource}, subscribes to them one after the other terminates @@ -58,12 +56,11 @@ public FlowableConcatMapMaybe(Flowable source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatMapMaybeSubscriber(s, mapper, prefetch, errorMode)); + source.subscribe(new ConcatMapMaybeSubscriber<>(s, mapper, prefetch, errorMode)); } static final class ConcatMapMaybeSubscriber - extends AtomicInteger - implements FlowableSubscriber, Subscription { + extends ConcatMapXMainSubscriber implements Subscription { private static final long serialVersionUID = -9140123220065488293L; @@ -71,24 +68,10 @@ static final class ConcatMapMaybeSubscriber final Function> mapper; - final int prefetch; - final AtomicLong requested; - final AtomicThrowable errors; - final ConcatMapMaybeObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Subscription upstream; - - volatile boolean done; - - volatile boolean cancelled; - long emitted; int consumed; @@ -107,50 +90,16 @@ static final class ConcatMapMaybeSubscriber ConcatMapMaybeSubscriber(Subscriber downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.prefetch = prefetch; - this.errorMode = errorMode; this.requested = new AtomicLong(); - this.errors = new AtomicThrowable(); - this.inner = new ConcatMapMaybeObserver(this); - this.queue = new SpscArrayQueue(prefetch); - } - - @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(upstream, s)) { - upstream = s; - downstream.onSubscribe(this); - s.request(prefetch); - } - } - - @Override - public void onNext(T t) { - if (!queue.offer(t)) { - upstream.cancel(); - onError(new MissingBackpressureException("queue full?!")); - return; - } - drain(); - } - - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - } - done = true; - drain(); - } + this.inner = new ConcatMapMaybeObserver<>(this); } @Override - public void onComplete() { - done = true; - drain(); + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override @@ -161,14 +110,7 @@ public void request(long n) { @Override public void cancel() { - cancelled = true; - upstream.cancel(); - inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } + stop(); } void innerSuccess(R item) { @@ -192,6 +134,17 @@ void innerError(Throwable ex) { } } + @Override + void clearValue() { + item = null; + } + + @Override + void disposeInner() { + inner.dispose(); + } + + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -200,10 +153,11 @@ void drain() { int missed = 1; Subscriber downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; AtomicLong requested = this.requested; int limit = prefetch - (prefetch >> 1); + boolean syncFused = this.syncFused; for (;;) { @@ -228,7 +182,16 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -240,18 +203,20 @@ void drain() { break; } - int c = consumed + 1; - if (c == limit) { - consumed = 0; - upstream.request(limit); - } else { - consumed = c; + if (!syncFused) { + int c = consumed + 1; + if (c == limit) { + consumed = 0; + upstream.request(limit); + } else { + consumed = c; + } } MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybePublisher.java similarity index 51% rename from src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapPublisher.java rename to src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybePublisher.java index 8ee6afcd87..42c1f732c2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybePublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,40 +10,46 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; + +package io.reactivex.rxjava3.internal.operators.mixed; import org.reactivestreams.*; -import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.operators.mixed.FlowableConcatMapMaybe.ConcatMapMaybeSubscriber; import io.reactivex.rxjava3.internal.util.ErrorMode; -public final class FlowableConcatMapPublisher extends Flowable { +/** + * Maps each upstream item into a {@link MaybeSource}, subscribes to them one after the other terminates + * and relays their success values, optionally delaying any errors till the main and inner sources + * terminate. + *

History: 2.1.11 - experimental + * @param the upstream element type + * @param the output element type + * @since 2.2 + */ +public final class FlowableConcatMapMaybePublisher extends Flowable { final Publisher source; - final Function> mapper; - - final int prefetch; + final Function> mapper; final ErrorMode errorMode; - public FlowableConcatMapPublisher(Publisher source, - Function> mapper, - int prefetch, ErrorMode errorMode) { + final int prefetch; + + public FlowableConcatMapMaybePublisher(Publisher source, + Function> mapper, + ErrorMode errorMode, int prefetch) { this.source = source; this.mapper = mapper; - this.prefetch = prefetch; this.errorMode = errorMode; + this.prefetch = prefetch; } @Override protected void subscribeActual(Subscriber s) { - - if (FlowableScalarXMap.tryScalarXMapSubscribe(source, s, mapper)) { - return; - } - - source.subscribe(FlowableConcatMap.subscribe(s, mapper, prefetch, errorMode)); + source.subscribe(new ConcatMapMaybeSubscriber<>(s, mapper, prefetch, errorMode)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java index cd5e857274..5c18b6b258 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,20 +13,18 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; -import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Maps each upstream item into a {@link SingleSource}, subscribes to them one after the other terminates @@ -58,12 +56,11 @@ public FlowableConcatMapSingle(Flowable source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new ConcatMapSingleSubscriber(s, mapper, prefetch, errorMode)); + source.subscribe(new ConcatMapSingleSubscriber<>(s, mapper, prefetch, errorMode)); } static final class ConcatMapSingleSubscriber - extends AtomicInteger - implements FlowableSubscriber, Subscription { + extends ConcatMapXMainSubscriber implements Subscription { private static final long serialVersionUID = -9140123220065488293L; @@ -71,24 +68,10 @@ static final class ConcatMapSingleSubscriber final Function> mapper; - final int prefetch; - final AtomicLong requested; - final AtomicThrowable errors; - final ConcatMapSingleObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Subscription upstream; - - volatile boolean done; - - volatile boolean cancelled; - long emitted; int consumed; @@ -107,68 +90,37 @@ static final class ConcatMapSingleSubscriber ConcatMapSingleSubscriber(Subscriber downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.prefetch = prefetch; - this.errorMode = errorMode; this.requested = new AtomicLong(); - this.errors = new AtomicThrowable(); - this.inner = new ConcatMapSingleObserver(this); - this.queue = new SpscArrayQueue(prefetch); + this.inner = new ConcatMapSingleObserver<>(this); } @Override - public void onSubscribe(Subscription s) { - if (SubscriptionHelper.validate(upstream, s)) { - upstream = s; - downstream.onSubscribe(this); - s.request(prefetch); - } + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void onNext(T t) { - if (!queue.offer(t)) { - upstream.cancel(); - onError(new MissingBackpressureException("queue full?!")); - return; - } + public void request(long n) { + BackpressureHelper.add(requested, n); drain(); } @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - } - done = true; - drain(); - } - } - - @Override - public void onComplete() { - done = true; - drain(); + public void cancel() { + stop(); } @Override - public void request(long n) { - BackpressureHelper.add(requested, n); - drain(); + void clearValue() { + item = null; } @Override - public void cancel() { - cancelled = true; - upstream.cancel(); + void disposeInner() { inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } } void innerSuccess(R item) { @@ -187,6 +139,7 @@ void innerError(Throwable ex) { } } + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -195,10 +148,11 @@ void drain() { int missed = 1; Subscriber downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; AtomicLong requested = this.requested; int limit = prefetch - (prefetch >> 1); + boolean syncFused = this.syncFused; for (;;) { @@ -223,7 +177,16 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.cancel(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -235,18 +198,20 @@ void drain() { break; } - int c = consumed + 1; - if (c == limit) { - consumed = 0; - upstream.request(limit); - } else { - consumed = c; + if (!syncFused) { + int c = consumed + 1; + if (c == limit) { + consumed = 0; + upstream.request(limit); + } else { + consumed = c; + } } SingleSource ss; try { - ss = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null SingleSource"); + ss = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSinglePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSinglePublisher.java new file mode 100644 index 0000000000..0e1986180a --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSinglePublisher.java @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.operators.mixed.FlowableConcatMapSingle.ConcatMapSingleSubscriber; +import io.reactivex.rxjava3.internal.util.ErrorMode; + +/** + * Maps each upstream item into a {@link SingleSource}, subscribes to them one after the other terminates + * and relays their success values, optionally delaying any errors till the main and inner sources + * terminate. + *

History: 2.1.11 - experimental + * @param the upstream element type + * @param the output element type + * @since 2.2 + */ +public final class FlowableConcatMapSinglePublisher extends Flowable { + + final Publisher source; + + final Function> mapper; + + final ErrorMode errorMode; + + final int prefetch; + + public FlowableConcatMapSinglePublisher(Publisher source, + Function> mapper, + ErrorMode errorMode, int prefetch) { + this.source = source; + this.mapper = mapper; + this.errorMode = errorMode; + this.prefetch = prefetch; + } + + @Override + protected void subscribeActual(Subscriber s) { + source.subscribe(new ConcatMapSingleSubscriber<>(s, mapper, prefetch, errorMode)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletable.java index f354d8f057..9ca61ab15a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import org.reactivestreams.Subscription; @@ -22,7 +23,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -52,7 +52,7 @@ public FlowableSwitchMapCompletable(Flowable source, @Override protected void subscribeActual(CompletableObserver observer) { - source.subscribe(new SwitchMapCompletableObserver(observer, mapper, delayErrors)); + source.subscribe(new SwitchMapCompletableObserver<>(observer, mapper, delayErrors)); } static final class SwitchMapCompletableObserver implements FlowableSubscriber, Disposable { @@ -79,7 +79,7 @@ static final class SwitchMapCompletableObserver implements FlowableSubscriber this.mapper = mapper; this.delayErrors = delayErrors; this.errors = new AtomicThrowable(); - this.inner = new AtomicReference(); + this.inner = new AtomicReference<>(); } @Override @@ -96,7 +96,7 @@ public void onNext(T t) { CompletableSource c; try { - c = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null CompletableSource"); + c = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletablePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletablePublisher.java new file mode 100644 index 0000000000..9eae52029e --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletablePublisher.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import org.reactivestreams.Publisher; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; + +/** + * Switch between subsequent {@link CompletableSource}s emitted by a {@link Publisher}. + * Reuses {@link FlowableSwitchMapCompletable} internals. + * @param the upstream value type + * @since 3.0.0 + */ +public final class FlowableSwitchMapCompletablePublisher extends Completable { + + final Publisher source; + + final Function mapper; + + final boolean delayErrors; + + public FlowableSwitchMapCompletablePublisher(Publisher source, + Function mapper, boolean delayErrors) { + this.source = source; + this.mapper = mapper; + this.delayErrors = delayErrors; + } + + @Override + protected void subscribeActual(CompletableObserver observer) { + source.subscribe(new FlowableSwitchMapCompletable.SwitchMapCompletableObserver<>(observer, mapper, delayErrors)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybe.java index 56858d5d22..3eab4e0d62 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,7 +23,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -54,7 +54,7 @@ public FlowableSwitchMapMaybe(Flowable source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SwitchMapMaybeSubscriber(s, mapper, delayErrors)); + source.subscribe(new SwitchMapMaybeSubscriber<>(s, mapper, delayErrors)); } static final class SwitchMapMaybeSubscriber extends AtomicInteger @@ -75,7 +75,7 @@ static final class SwitchMapMaybeSubscriber extends AtomicInteger final AtomicReference> inner; static final SwitchMapMaybeObserver INNER_DISPOSED = - new SwitchMapMaybeObserver(null); + new SwitchMapMaybeObserver<>(null); Subscription upstream; @@ -93,7 +93,7 @@ static final class SwitchMapMaybeSubscriber extends AtomicInteger this.delayErrors = delayErrors; this.errors = new AtomicThrowable(); this.requested = new AtomicLong(); - this.inner = new AtomicReference>(); + this.inner = new AtomicReference<>(); } @Override @@ -116,7 +116,7 @@ public void onNext(T t) { MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -125,7 +125,7 @@ public void onNext(T t) { return; } - SwitchMapMaybeObserver observer = new SwitchMapMaybeObserver(this); + SwitchMapMaybeObserver observer = new SwitchMapMaybeObserver<>(this); for (;;) { current = inner.get(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybePublisher.java new file mode 100644 index 0000000000..319247c295 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybePublisher.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; + +/** + * Switch between subsequent {@link MaybeSource}s emitted by a {@link Publisher}. + * Reuses {@link FlowableSwitchMapMaybe} internals. + * @param the upstream value type + * @param the downstream value type + * @since 3.0.0 + */ +public final class FlowableSwitchMapMaybePublisher extends Flowable { + + final Publisher source; + + final Function> mapper; + + final boolean delayErrors; + + public FlowableSwitchMapMaybePublisher(Publisher source, + Function> mapper, + boolean delayErrors) { + this.source = source; + this.mapper = mapper; + this.delayErrors = delayErrors; + } + + @Override + protected void subscribeActual(Subscriber s) { + source.subscribe(new FlowableSwitchMapMaybe.SwitchMapMaybeSubscriber<>(s, mapper, delayErrors)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingle.java index ac0880082b..59c9cb7f8f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,7 +23,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -54,7 +54,7 @@ public FlowableSwitchMapSingle(Flowable source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new SwitchMapSingleSubscriber(s, mapper, delayErrors)); + source.subscribe(new SwitchMapSingleSubscriber<>(s, mapper, delayErrors)); } static final class SwitchMapSingleSubscriber extends AtomicInteger @@ -75,7 +75,7 @@ static final class SwitchMapSingleSubscriber extends AtomicInteger final AtomicReference> inner; static final SwitchMapSingleObserver INNER_DISPOSED = - new SwitchMapSingleObserver(null); + new SwitchMapSingleObserver<>(null); Subscription upstream; @@ -93,7 +93,7 @@ static final class SwitchMapSingleSubscriber extends AtomicInteger this.delayErrors = delayErrors; this.errors = new AtomicThrowable(); this.requested = new AtomicLong(); - this.inner = new AtomicReference>(); + this.inner = new AtomicReference<>(); } @Override @@ -116,7 +116,7 @@ public void onNext(T t) { SingleSource ss; try { - ss = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); + ss = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.cancel(); @@ -125,7 +125,7 @@ public void onNext(T t) { return; } - SwitchMapSingleObserver observer = new SwitchMapSingleObserver(this); + SwitchMapSingleObserver observer = new SwitchMapSingleObserver<>(this); for (;;) { current = inner.get(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSinglePublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSinglePublisher.java new file mode 100644 index 0000000000..31ace1bf2d --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSinglePublisher.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.mixed; + +import org.reactivestreams.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Function; + +/** + * Switch between subsequent {@link SingleSource}s emitted by a {@link Publisher}. + * Reuses {@link FlowableSwitchMapSingle} internals. + * @param the upstream value type + * @param the downstream value type + * @since 3.0.0 + */ +public final class FlowableSwitchMapSinglePublisher extends Flowable { + + final Publisher source; + + final Function> mapper; + + final boolean delayErrors; + + public FlowableSwitchMapSinglePublisher(Publisher source, + Function> mapper, + boolean delayErrors) { + this.source = source; + this.mapper = mapper; + this.delayErrors = delayErrors; + } + + @Override + protected void subscribeActual(Subscriber s) { + source.subscribe(new FlowableSwitchMapSingle.SwitchMapSingleSubscriber<>(s, mapper, delayErrors)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaterializeSingleObserver.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaterializeSingleObserver.java index 551b32d4b1..3f1c540d7d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaterializeSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaterializeSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,17 +45,17 @@ public void onSubscribe(Disposable d) { @Override public void onComplete() { - downstream.onSuccess(Notification.createOnComplete()); + downstream.onSuccess(Notification.createOnComplete()); } @Override public void onSuccess(T t) { - downstream.onSuccess(Notification.createOnNext(t)); + downstream.onSuccess(Notification.createOnNext(t)); } @Override public void onError(Throwable e) { - downstream.onSuccess(Notification.createOnError(e)); + downstream.onSuccess(Notification.createOnError(e)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservable.java index a4dde1242f..09e634f944 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,12 +21,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps the success value of a Maybe onto an ObservableSource and * relays its signals to the downstream observer. - * + * * @param the success value type of the Maybe source * @param the result type of the ObservableSource and this operator * @since 2.1.15 @@ -44,7 +44,7 @@ public MaybeFlatMapObservable(MaybeSource source, @Override protected void subscribeActual(Observer observer) { - FlatMapObserver parent = new FlatMapObserver(observer, mapper); + FlatMapObserver parent = new FlatMapObserver<>(observer, mapper); observer.onSubscribe(parent); source.subscribe(parent); } @@ -99,14 +99,16 @@ public void onSuccess(T t) { ObservableSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + o = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); return; } - o.subscribe(this); + if (!isDisposed()) { + o.subscribe(this); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisher.java index 66a446f3f3..b6d2afeb2d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -22,13 +23,12 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; /** * Maps the success value of a Maybe onto a Publisher and * relays its signals to the downstream subscriber. - * + * * @param the success value type of the Maybe source * @param the result type of the Publisher and this operator * @since 2.1.15 @@ -47,7 +47,7 @@ public MaybeFlatMapPublisher(MaybeSource source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new FlatMapPublisherSubscriber(s, mapper)); + source.subscribe(new FlatMapPublisherSubscriber<>(s, mapper)); } static final class FlatMapPublisherSubscriber @@ -109,14 +109,16 @@ public void onSuccess(T t) { Publisher p; try { - p = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + p = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); return; } - p.subscribe(this); + if (get() != SubscriptionHelper.CANCELLED) { + p.subscribe(this); + } } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java index 4921722b5d..f9d91b170a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,17 +13,16 @@ package io.reactivex.rxjava3.internal.operators.mixed; -import java.util.concurrent.atomic.*; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Maps the upstream items into {@link CompletableSource}s and subscribes to them one after the @@ -55,13 +54,12 @@ public ObservableConcatMapCompletable(Observable source, @Override protected void subscribeActual(CompletableObserver observer) { if (!ScalarXMapZHelper.tryAsCompletable(source, mapper, observer)) { - source.subscribe(new ConcatMapCompletableObserver(observer, mapper, errorMode, prefetch)); + source.subscribe(new ConcatMapCompletableObserver<>(observer, mapper, errorMode, prefetch)); } } static final class ConcatMapCompletableObserver - extends AtomicInteger - implements Observer, Disposable { + extends ConcatMapXMainObserver { private static final long serialVersionUID = 3610901111000061034L; @@ -69,122 +67,36 @@ static final class ConcatMapCompletableObserver final Function mapper; - final ErrorMode errorMode; - - final AtomicThrowable errors; - final ConcatMapInnerObserver inner; - final int prefetch; - - SimpleQueue queue; - - Disposable upstream; - volatile boolean active; - volatile boolean done; - - volatile boolean disposed; - ConcatMapCompletableObserver(CompletableObserver downstream, Function mapper, ErrorMode errorMode, int prefetch) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.prefetch = prefetch; - this.errors = new AtomicThrowable(); this.inner = new ConcatMapInnerObserver(this); } @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - this.upstream = d; - if (d instanceof QueueDisposable) { - @SuppressWarnings("unchecked") - QueueDisposable qd = (QueueDisposable) d; - - int m = qd.requestFusion(QueueDisposable.ANY); - if (m == QueueDisposable.SYNC) { - queue = qd; - done = true; - downstream.onSubscribe(this); - drain(); - return; - } - if (m == QueueDisposable.ASYNC) { - queue = qd; - downstream.onSubscribe(this); - return; - } - } - queue = new SpscLinkedArrayQueue(prefetch); - downstream.onSubscribe(this); - } - } - - @Override - public void onNext(T t) { - if (t != null) { - queue.offer(t); - } - drain(); + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - disposed = true; - inner.dispose(); - errors.tryTerminateConsumer(downstream); - if (getAndIncrement() == 0) { - queue.clear(); - } - } else { - done = true; - drain(); - } - } - } - - @Override - public void onComplete() { - done = true; - drain(); - } - - @Override - public void dispose() { - disposed = true; - upstream.dispose(); + void disposeInner() { inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - } - } - - @Override - public boolean isDisposed() { - return disposed; } void innerError(Throwable ex) { if (errors.tryAddThrowableOrReport(ex)) { - if (errorMode == ErrorMode.IMMEDIATE) { - disposed = true; + if (errorMode != ErrorMode.END) { upstream.dispose(); - errors.tryTerminateConsumer(downstream); - if (getAndIncrement() == 0) { - queue.clear(); - } - } else { - active = false; - drain(); } + active = false; + drain(); } } @@ -193,6 +105,7 @@ void innerComplete() { drain(); } + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -200,6 +113,7 @@ void drain() { AtomicThrowable errors = this.errors; ErrorMode errorMode = this.errorMode; + SimpleQueue queue = this.queue; do { if (disposed) { @@ -207,16 +121,17 @@ void drain() { return; } - if (!active) { - - if (errorMode == ErrorMode.BOUNDARY) { - if (errors.get() != null) { - disposed = true; - queue.clear(); - errors.tryTerminateConsumer(downstream); - return; - } + if (errors.get() != null) { + if (errorMode == ErrorMode.IMMEDIATE + || (errorMode == ErrorMode.BOUNDARY && !active)) { + disposed = true; + queue.clear(); + errors.tryTerminateConsumer(downstream); + return; } + } + + if (!active) { boolean d = done; boolean empty = true; @@ -224,7 +139,7 @@ void drain() { try { T v = queue.poll(); if (v != null) { - cs = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null CompletableSource"); empty = false; } } catch (Throwable ex) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java index 17e4de9050..7e85104dd7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,17 +13,16 @@ package io.reactivex.rxjava3.internal.operators.mixed; -import java.util.concurrent.atomic.*; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Maps each upstream item into a {@link MaybeSource}, subscribes to them one after the other terminates @@ -56,13 +55,12 @@ public ObservableConcatMapMaybe(Observable source, @Override protected void subscribeActual(Observer observer) { if (!ScalarXMapZHelper.tryAsMaybe(source, mapper, observer)) { - source.subscribe(new ConcatMapMaybeMainObserver(observer, mapper, prefetch, errorMode)); + source.subscribe(new ConcatMapMaybeMainObserver<>(observer, mapper, prefetch, errorMode)); } } static final class ConcatMapMaybeMainObserver - extends AtomicInteger - implements Observer, Disposable { + extends ConcatMapXMainObserver { private static final long serialVersionUID = -9140123220065488293L; @@ -70,20 +68,8 @@ static final class ConcatMapMaybeMainObserver final Function> mapper; - final AtomicThrowable errors; - final ConcatMapMaybeObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Disposable upstream; - - volatile boolean done; - - volatile boolean cancelled; - R item; volatile int state; @@ -98,60 +84,20 @@ static final class ConcatMapMaybeMainObserver ConcatMapMaybeMainObserver(Observer downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.errors = new AtomicThrowable(); - this.inner = new ConcatMapMaybeObserver(this); - this.queue = new SpscLinkedArrayQueue(prefetch); + this.inner = new ConcatMapMaybeObserver<>(this); } @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - upstream = d; - downstream.onSubscribe(this); - } + void onSubscribeDownstream() { + downstream.onSubscribe(this); } @Override - public void onNext(T t) { - queue.offer(t); - drain(); - } - - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); - } - done = true; - drain(); - } - } - - @Override - public void onComplete() { - done = true; - drain(); - } - - @Override - public void dispose() { - cancelled = true; - upstream.dispose(); - inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } - } - - @Override - public boolean isDisposed() { - return cancelled; + void clearValue() { + item = null; } void innerSuccess(R item) { @@ -175,6 +121,12 @@ void innerError(Throwable ex) { } } + @Override + void disposeInner() { + inner.dispose(); + } + + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -183,13 +135,13 @@ void drain() { int missed = 1; Observer downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; for (;;) { for (;;) { - if (cancelled) { + if (disposed) { queue.clear(); item = null; break; @@ -209,7 +161,18 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + disposed = true; + upstream.dispose(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -224,7 +187,7 @@ void drain() { MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java index 91428a80db..b57940a5e7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,17 +13,16 @@ package io.reactivex.rxjava3.internal.operators.mixed; -import java.util.concurrent.atomic.*; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Maps each upstream item into a {@link SingleSource}, subscribes to them one after the other terminates @@ -36,7 +35,7 @@ */ public final class ObservableConcatMapSingle extends Observable { - final Observable source; + final ObservableSource source; final Function> mapper; @@ -44,7 +43,7 @@ public final class ObservableConcatMapSingle extends Observable { final int prefetch; - public ObservableConcatMapSingle(Observable source, + public ObservableConcatMapSingle(ObservableSource source, Function> mapper, ErrorMode errorMode, int prefetch) { this.source = source; @@ -56,13 +55,12 @@ public ObservableConcatMapSingle(Observable source, @Override protected void subscribeActual(Observer observer) { if (!ScalarXMapZHelper.tryAsSingle(source, mapper, observer)) { - source.subscribe(new ConcatMapSingleMainObserver(observer, mapper, prefetch, errorMode)); + source.subscribe(new ConcatMapSingleMainObserver<>(observer, mapper, prefetch, errorMode)); } } static final class ConcatMapSingleMainObserver - extends AtomicInteger - implements Observer, Disposable { + extends ConcatMapXMainObserver { private static final long serialVersionUID = -9140123220065488293L; @@ -70,20 +68,8 @@ static final class ConcatMapSingleMainObserver final Function> mapper; - final AtomicThrowable errors; - final ConcatMapSingleObserver inner; - final SimplePlainQueue queue; - - final ErrorMode errorMode; - - Disposable upstream; - - volatile boolean done; - - volatile boolean cancelled; - R item; volatile int state; @@ -98,78 +84,44 @@ static final class ConcatMapSingleMainObserver ConcatMapSingleMainObserver(Observer downstream, Function> mapper, int prefetch, ErrorMode errorMode) { + super(prefetch, errorMode); this.downstream = downstream; this.mapper = mapper; - this.errorMode = errorMode; - this.errors = new AtomicThrowable(); - this.inner = new ConcatMapSingleObserver(this); - this.queue = new SpscLinkedArrayQueue(prefetch); + this.inner = new ConcatMapSingleObserver<>(this); } - @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(upstream, d)) { - upstream = d; - downstream.onSubscribe(this); - } - } - - @Override - public void onNext(T t) { - queue.offer(t); + void innerSuccess(R item) { + this.item = item; + this.state = STATE_RESULT_VALUE; drain(); } - @Override - public void onError(Throwable t) { - if (errors.tryAddThrowableOrReport(t)) { - if (errorMode == ErrorMode.IMMEDIATE) { - inner.dispose(); + void innerError(Throwable ex) { + if (errors.tryAddThrowableOrReport(ex)) { + if (errorMode != ErrorMode.END) { + upstream.dispose(); } - done = true; + this.state = STATE_INACTIVE; drain(); } } @Override - public void onComplete() { - done = true; - drain(); - } - - @Override - public void dispose() { - cancelled = true; - upstream.dispose(); + void disposeInner() { inner.dispose(); - errors.tryTerminateAndReport(); - if (getAndIncrement() == 0) { - queue.clear(); - item = null; - } } @Override - public boolean isDisposed() { - return cancelled; + void onSubscribeDownstream() { + downstream.onSubscribe(this); } - void innerSuccess(R item) { - this.item = item; - this.state = STATE_RESULT_VALUE; - drain(); - } - - void innerError(Throwable ex) { - if (errors.tryAddThrowableOrReport(ex)) { - if (errorMode != ErrorMode.END) { - upstream.dispose(); - } - this.state = STATE_INACTIVE; - drain(); - } + @Override + void clearValue() { + item = null; } + @Override void drain() { if (getAndIncrement() != 0) { return; @@ -178,13 +130,13 @@ void drain() { int missed = 1; Observer downstream = this.downstream; ErrorMode errorMode = this.errorMode; - SimplePlainQueue queue = this.queue; + SimpleQueue queue = this.queue; AtomicThrowable errors = this.errors; for (;;) { for (;;) { - if (cancelled) { + if (disposed) { queue.clear(); item = null; break; @@ -204,7 +156,18 @@ void drain() { if (s == STATE_INACTIVE) { boolean d = done; - T v = queue.poll(); + T v; + + try { + v = queue.poll(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + disposed = true; + upstream.dispose(); + errors.tryAddThrowableOrReport(ex); + errors.tryTerminateConsumer(downstream); + return; + } boolean empty = v == null; if (d && empty) { @@ -219,7 +182,7 @@ void drain() { SingleSource ss; try { - ss = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null SingleSource"); + ss = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletable.java index 06a09676d0..b751afdbb0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -50,7 +50,7 @@ public ObservableSwitchMapCompletable(Observable source, @Override protected void subscribeActual(CompletableObserver observer) { if (!ScalarXMapZHelper.tryAsCompletable(source, mapper, observer)) { - source.subscribe(new SwitchMapCompletableObserver(observer, mapper, delayErrors)); + source.subscribe(new SwitchMapCompletableObserver<>(observer, mapper, delayErrors)); } } @@ -78,7 +78,7 @@ static final class SwitchMapCompletableObserver implements Observer, Dispo this.mapper = mapper; this.delayErrors = delayErrors; this.errors = new AtomicThrowable(); - this.inner = new AtomicReference(); + this.inner = new AtomicReference<>(); } @Override @@ -94,7 +94,7 @@ public void onNext(T t) { CompletableSource c; try { - c = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null CompletableSource"); + c = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybe.java index b85bc0e2c2..fe394df1e8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -52,7 +52,7 @@ public ObservableSwitchMapMaybe(Observable source, @Override protected void subscribeActual(Observer observer) { if (!ScalarXMapZHelper.tryAsMaybe(source, mapper, observer)) { - source.subscribe(new SwitchMapMaybeMainObserver(observer, mapper, delayErrors)); + source.subscribe(new SwitchMapMaybeMainObserver<>(observer, mapper, delayErrors)); } } @@ -72,7 +72,7 @@ static final class SwitchMapMaybeMainObserver extends AtomicInteger final AtomicReference> inner; static final SwitchMapMaybeObserver INNER_DISPOSED = - new SwitchMapMaybeObserver(null); + new SwitchMapMaybeObserver<>(null); Disposable upstream; @@ -87,7 +87,7 @@ static final class SwitchMapMaybeMainObserver extends AtomicInteger this.mapper = mapper; this.delayErrors = delayErrors; this.errors = new AtomicThrowable(); - this.inner = new AtomicReference>(); + this.inner = new AtomicReference<>(); } @Override @@ -109,7 +109,7 @@ public void onNext(T t) { MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -118,7 +118,7 @@ public void onNext(T t) { return; } - SwitchMapMaybeObserver observer = new SwitchMapMaybeObserver(this); + SwitchMapMaybeObserver observer = new SwitchMapMaybeObserver<>(this); for (;;) { current = inner.get(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingle.java index 3343f93d97..ca0e2953e0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -52,7 +52,7 @@ public ObservableSwitchMapSingle(Observable source, @Override protected void subscribeActual(Observer observer) { if (!ScalarXMapZHelper.tryAsSingle(source, mapper, observer)) { - source.subscribe(new SwitchMapSingleMainObserver(observer, mapper, delayErrors)); + source.subscribe(new SwitchMapSingleMainObserver<>(observer, mapper, delayErrors)); } } @@ -72,7 +72,7 @@ static final class SwitchMapSingleMainObserver extends AtomicInteger final AtomicReference> inner; static final SwitchMapSingleObserver INNER_DISPOSED = - new SwitchMapSingleObserver(null); + new SwitchMapSingleObserver<>(null); Disposable upstream; @@ -87,7 +87,7 @@ static final class SwitchMapSingleMainObserver extends AtomicInteger this.mapper = mapper; this.delayErrors = delayErrors; this.errors = new AtomicThrowable(); - this.inner = new AtomicReference>(); + this.inner = new AtomicReference<>(); } @Override @@ -109,7 +109,7 @@ public void onNext(T t) { SingleSource ss; try { - ss = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); + ss = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -118,7 +118,7 @@ public void onNext(T t) { return; } - SwitchMapSingleObserver observer = new SwitchMapSingleObserver(this); + SwitchMapSingleObserver observer = new SwitchMapSingleObserver<>(this); for (;;) { current = inner.get(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java index 24f231e45c..2ed6301e42 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.maybe.MaybeToObservable; import io.reactivex.rxjava3.internal.operators.single.SingleToObservable; +import java.util.Objects; + /** * Utility class to extract a value from a scalar source reactive type, * map it to a 0-1 type then subscribe the output type's consumer to it, @@ -55,7 +56,7 @@ static boolean tryAsCompletable(Object source, try { T item = supplier.get(); if (item != null) { - cs = ObjectHelper.requireNonNull(mapper.apply(item), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(item), "The mapper returned a null CompletableSource"); } } catch (Throwable ex) { Exceptions.throwIfFatal(ex); @@ -77,6 +78,7 @@ static boolean tryAsCompletable(Object source, * Try subscribing to a {@link MaybeSource} mapped from * a scalar source (which implements {@link Supplier}). * @param the upstream value type + * @param the downstream value type * @param source the source reactive type ({@code Flowable} or {@code Observable}) * possibly implementing {@link Supplier}. * @param mapper the function that turns the scalar upstream value into a @@ -94,7 +96,7 @@ static boolean tryAsMaybe(Object source, try { T item = supplier.get(); if (item != null) { - cs = ObjectHelper.requireNonNull(mapper.apply(item), "The mapper returned a null MaybeSource"); + cs = Objects.requireNonNull(mapper.apply(item), "The mapper returned a null MaybeSource"); } } catch (Throwable ex) { Exceptions.throwIfFatal(ex); @@ -116,6 +118,7 @@ static boolean tryAsMaybe(Object source, * Try subscribing to a {@link SingleSource} mapped from * a scalar source (which implements {@link Supplier}). * @param the upstream value type + * @param the downstream value type * @param source the source reactive type ({@code Flowable} or {@code Observable}) * possibly implementing {@link Supplier}. * @param mapper the function that turns the scalar upstream value into a @@ -133,7 +136,7 @@ static boolean tryAsSingle(Object source, try { T item = supplier.get(); if (item != null) { - cs = ObjectHelper.requireNonNull(mapper.apply(item), "The mapper returned a null SingleSource"); + cs = Objects.requireNonNull(mapper.apply(item), "The mapper returned a null SingleSource"); } } catch (Throwable ex) { Exceptions.throwIfFatal(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservable.java index 9591783a8e..5db7515890 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.mixed; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,12 +21,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps the success value of a Single onto an ObservableSource and * relays its signals to the downstream observer. - * + * * @param the success value type of the Single source * @param the result type of the ObservableSource and this operator * @since 2.1.15 @@ -44,7 +44,7 @@ public SingleFlatMapObservable(SingleSource source, @Override protected void subscribeActual(Observer observer) { - FlatMapObserver parent = new FlatMapObserver(observer, mapper); + FlatMapObserver parent = new FlatMapObserver<>(observer, mapper); observer.onSubscribe(parent); source.subscribe(parent); } @@ -99,14 +99,16 @@ public void onSuccess(T t) { ObservableSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); + o = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Publisher"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); return; } - o.subscribe(this); + if (!isDisposed()) { + o.subscribe(this); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstream.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstream.java index 2225cb2d2a..dcad29a5d8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstream.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstream.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableIterable.java index 0ba2da1720..561571febf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,8 +20,8 @@ import io.reactivex.rxjava3.core.ObservableSource; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class BlockingObservableIterable implements Iterable { final ObservableSource source; @@ -35,7 +35,7 @@ public BlockingObservableIterable(ObservableSource source, int buff @Override public Iterator iterator() { - BlockingObservableIterator it = new BlockingObservableIterator(bufferSize); + BlockingObservableIterator it = new BlockingObservableIterator<>(bufferSize); source.subscribe(it); return it; } @@ -56,7 +56,7 @@ static final class BlockingObservableIterator volatile Throwable error; BlockingObservableIterator(int batchSize) { - this.queue = new SpscLinkedArrayQueue(batchSize); + this.queue = new SpscLinkedArrayQueue<>(batchSize); this.lock = new ReentrantLock(); this.condition = lock.newCondition(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatest.java index aaa975dbf7..4aca95b210 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public BlockingObservableLatest(ObservableSource source) { @Override public Iterator iterator() { - BlockingObservableLatestIterator lio = new BlockingObservableLatestIterator(); + BlockingObservableLatestIterator lio = new BlockingObservableLatestIterator<>(); Observable> materialized = Observable.wrap(source).materialize(); @@ -52,7 +52,7 @@ static final class BlockingObservableLatestIterator extends DisposableObserve final Semaphore notify = new Semaphore(0); // observer's notification - final AtomicReference> value = new AtomicReference>(); + final AtomicReference> value = new AtomicReference<>(); @Override public void onNext(Notification args) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java index 20408d2607..0b2ea5bd34 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecent.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ * Returns an Iterable that always returns the item most recently emitted by an Observable, or a * seed value if no item has yet been emitted. *

- * + * * * @param the value type */ @@ -40,7 +40,7 @@ public BlockingObservableMostRecent(ObservableSource source, T initialValue) @Override public Iterator iterator() { - MostRecentObserver mostRecentObserver = new MostRecentObserver(initialValue); + MostRecentObserver mostRecentObserver = new MostRecentObserver<>(initialValue); source.subscribe(mostRecentObserver); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java index 1d5cca354b..2bbfe2b036 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ /** * Returns an Iterable that blocks until the Observable emits another item, then returns that item. *

- * + * * * @param the value type */ @@ -39,8 +39,8 @@ public BlockingObservableNext(ObservableSource source) { @Override public Iterator iterator() { - NextObserver nextObserver = new NextObserver(); - return new NextIterator(source, nextObserver); + NextObserver nextObserver = new NextObserver<>(); + return new NextIterator<>(source, nextObserver); } // test needs to access the observer.waiting flag @@ -80,7 +80,7 @@ private boolean moveToNext() { started = true; // if not started, start now observer.setWaiting(); - new ObservableMaterialize(items).subscribe(observer); + new ObservableMaterialize<>(items).subscribe(observer); } Notification nextNotification; @@ -130,7 +130,7 @@ public void remove() { } static final class NextObserver extends DisposableObserver> { - private final BlockingQueue> buf = new ArrayBlockingQueue>(1); + private final BlockingQueue> buf = new ArrayBlockingQueue<>(1); final AtomicInteger waiting = new AtomicInteger(); @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAll.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAll.java index a28da3650d..0f365414c6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAll.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAll.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -28,7 +29,7 @@ public ObservableAll(ObservableSource source, Predicate predicate) @Override protected void subscribeActual(Observer t) { - source.subscribe(new AllObserver(t, predicate)); + source.subscribe(new AllObserver<>(t, predicate)); } static final class AllObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllSingle.java index db004ef966..89e4e38a16 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -31,12 +32,12 @@ public ObservableAllSingle(ObservableSource source, Predicate pred @Override protected void subscribeActual(SingleObserver t) { - source.subscribe(new AllObserver(t, predicate)); + source.subscribe(new AllObserver<>(t, predicate)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableAll(source, predicate)); + return RxJavaPlugins.onAssembly(new ObservableAll<>(source, predicate)); } static final class AllObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java index aa8fae849d..f8841956c6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmb.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -68,7 +68,7 @@ public void subscribeActual(Observer observer) { return; } - AmbCoordinator ac = new AmbCoordinator(observer, count); + AmbCoordinator ac = new AmbCoordinator<>(observer, count); ac.subscribe(sources); } @@ -88,7 +88,7 @@ public void subscribe(ObservableSource[] sources) { AmbInnerObserver[] as = observers; int len = as.length; for (int i = 0; i < len; i++) { - as[i] = new AmbInnerObserver(this, i + 1, downstream); + as[i] = new AmbInnerObserver<>(this, i + 1, downstream); } winner.lazySet(0); // release the contents of 'as' downstream.onSubscribe(this); @@ -115,9 +115,8 @@ public boolean win(int index) { } return true; } - return false; } - return w == index; + return false; } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAny.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAny.java index 69d3298d40..fad20eb6e2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAny.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAny.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -28,7 +29,7 @@ public ObservableAny(ObservableSource source, Predicate predicate) @Override protected void subscribeActual(Observer t) { - source.subscribe(new AnyObserver(t, predicate)); + source.subscribe(new AnyObserver<>(t, predicate)); } static final class AnyObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnySingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnySingle.java index d3c3e495a5..ff7875fb54 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnySingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnySingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -32,12 +33,12 @@ public ObservableAnySingle(ObservableSource source, Predicate pred @Override protected void subscribeActual(SingleObserver t) { - source.subscribe(new AnyObserver(t, predicate)); + source.subscribe(new AnyObserver<>(t, predicate)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableAny(source, predicate)); + return RxJavaPlugins.onAssembly(new ObservableAny<>(source, predicate)); } static final class AnyObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnect.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnect.java index 393a9a3f94..d00a2fc291 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnect.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnect.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingSubscribe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingSubscribe.java index c35f927c0b..e173785760 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.*; import io.reactivex.rxjava3.core.*; @@ -38,11 +39,12 @@ private ObservableBlockingSubscribe() { * The call to dispose() is composed through. * @param observer the subscriber to forward events and calls to in the current thread * @param the value type + * @throws NullPointerException if {@code observer} is {@code null} */ public static void subscribe(ObservableSource o, Observer observer) { - final BlockingQueue queue = new LinkedBlockingQueue(); + final BlockingQueue queue = new LinkedBlockingQueue<>(); - BlockingObserver bs = new BlockingObserver(queue); + BlockingObserver bs = new BlockingObserver<>(queue); observer.onSubscribe(bs); o.subscribe(bs); @@ -75,7 +77,7 @@ public static void subscribe(ObservableSource o, Observer void subscribe(ObservableSource o) { BlockingIgnoringReceiver callback = new BlockingIgnoringReceiver(); - LambdaObserver ls = new LambdaObserver(Functions.emptyConsumer(), + LambdaObserver ls = new LambdaObserver<>(Functions.emptyConsumer(), callback, callback, Functions.emptyConsumer()); o.subscribe(ls); @@ -97,9 +99,9 @@ public static void subscribe(ObservableSource o) { */ public static void subscribe(ObservableSource o, final Consumer onNext, final Consumer onError, final Action onComplete) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(onError, "onError is null"); - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(onError, "onError is null"); + Objects.requireNonNull(onComplete, "onComplete is null"); subscribe(o, new LambdaObserver(onNext, onError, onComplete, Functions.emptyConsumer())); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBuffer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBuffer.java index d0ae0b89cc..6f94a97cc6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBuffer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBuffer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.ExceptionHelper; public final class ObservableBuffer> extends AbstractObservableWithUpstream { @@ -40,12 +39,12 @@ public ObservableBuffer(ObservableSource source, int count, int skip, Supplie @Override protected void subscribeActual(Observer t) { if (skip == count) { - BufferExactObserver bes = new BufferExactObserver(t, count, bufferSupplier); + BufferExactObserver bes = new BufferExactObserver<>(t, count, bufferSupplier); if (bes.createBuffer()) { source.subscribe(bes); } } else { - source.subscribe(new BufferSkipObserver(t, count, skip, bufferSupplier)); + source.subscribe(new BufferSkipObserver<>(t, count, skip, bufferSupplier)); } } @@ -68,7 +67,7 @@ static final class BufferExactObserver> imple boolean createBuffer() { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "Empty buffer supplied"); + b = Objects.requireNonNull(bufferSupplier.get(), "Empty buffer supplied"); } catch (Throwable t) { Exceptions.throwIfFatal(t); buffer = null; @@ -158,7 +157,7 @@ static final class BufferSkipObserver> this.count = count; this.skip = skip; this.bufferSupplier = bufferSupplier; - this.buffers = new ArrayDeque(); + this.buffers = new ArrayDeque<>(); } @Override @@ -187,6 +186,7 @@ public void onNext(T t) { try { b = ExceptionHelper.nullCheck(bufferSupplier.get(), "The bufferSupplier returned a null Collection."); } catch (Throwable e) { + Exceptions.throwIfFatal(e); buffers.clear(); upstream.dispose(); downstream.onError(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferBoundary.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferBoundary.java index 961c6cd374..4b0fafb188 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferBoundary.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferBoundary.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,9 +22,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableBufferBoundary, Open, Close> @@ -44,7 +43,7 @@ public ObservableBufferBoundary(ObservableSource source, ObservableSource t) { BufferBoundaryObserver parent = - new BufferBoundaryObserver( + new BufferBoundaryObserver<>( t, bufferOpen, bufferClose, bufferSupplier ); t.onSubscribe(parent); @@ -89,10 +88,10 @@ static final class BufferBoundaryObserver, Op this.bufferSupplier = bufferSupplier; this.bufferOpen = bufferOpen; this.bufferClose = bufferClose; - this.queue = new SpscLinkedArrayQueue(bufferSize()); + this.queue = new SpscLinkedArrayQueue<>(bufferSize()); this.observers = new CompositeDisposable(); - this.upstream = new AtomicReference(); - this.buffers = new LinkedHashMap(); + this.upstream = new AtomicReference<>(); + this.buffers = new LinkedHashMap<>(); this.errors = new AtomicThrowable(); } @@ -100,7 +99,7 @@ static final class BufferBoundaryObserver, Op public void onSubscribe(Disposable d) { if (DisposableHelper.setOnce(this.upstream, d)) { - BufferOpenObserver open = new BufferOpenObserver(this); + BufferOpenObserver open = new BufferOpenObserver<>(this); observers.add(open); bufferOpen.subscribe(open); @@ -172,8 +171,8 @@ void open(Open token) { ObservableSource p; C buf; try { - buf = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null Collection"); - p = ObjectHelper.requireNonNull(bufferClose.apply(token), "The bufferClose returned a null ObservableSource"); + buf = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null Collection"); + p = Objects.requireNonNull(bufferClose.apply(token), "The bufferClose returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); DisposableHelper.dispose(upstream); @@ -191,7 +190,7 @@ void open(Open token) { bufs.put(idx, buf); } - BufferCloseObserver bc = new BufferCloseObserver(this, idx); + BufferCloseObserver bc = new BufferCloseObserver<>(this, idx); observers.add(bc); p.subscribe(bc); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferExactBoundary.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferExactBoundary.java index 474e993653..bb65058908 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferExactBoundary.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferExactBoundary.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.Collection; +import java.util.Objects; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.QueueDrainObserver; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; import io.reactivex.rxjava3.internal.util.QueueDrainHelper; @@ -39,11 +39,11 @@ public ObservableBufferExactBoundary(ObservableSource source, ObservableSourc @Override protected void subscribeActual(Observer t) { - source.subscribe(new BufferExactBoundaryObserver(new SerializedObserver(t), bufferSupplier, boundary)); + source.subscribe(new BufferExactBoundaryObserver<>(new SerializedObserver<>(t), bufferSupplier, boundary)); } static final class BufferExactBoundaryObserver, B> - extends QueueDrainObserver implements Observer, Disposable { + extends QueueDrainObserver implements Disposable { final Supplier bufferSupplier; final ObservableSource boundary; @@ -56,7 +56,7 @@ static final class BufferExactBoundaryObserver actual, Supplier bufferSupplier, ObservableSource boundary) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.boundary = boundary; } @@ -69,7 +69,7 @@ public void onSubscribe(Disposable d) { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); cancelled = true; @@ -80,7 +80,7 @@ public void onSubscribe(Disposable d) { buffer = b; - BufferBoundaryObserver bs = new BufferBoundaryObserver(this); + BufferBoundaryObserver bs = new BufferBoundaryObserver<>(this); other = bs; downstream.onSubscribe(this); @@ -148,7 +148,7 @@ void next() { U next; try { - next = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + next = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java index 107b011b2d..7628a8f685 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.QueueDrainObserver; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; import io.reactivex.rxjava3.internal.util.QueueDrainHelper; @@ -56,16 +55,16 @@ public ObservableBufferTimed(ObservableSource source, long timespan, long tim @Override protected void subscribeActual(Observer t) { if (timespan == timeskip && maxSize == Integer.MAX_VALUE) { - source.subscribe(new BufferExactUnboundedObserver( - new SerializedObserver(t), + source.subscribe(new BufferExactUnboundedObserver<>( + new SerializedObserver<>(t), bufferSupplier, timespan, unit, scheduler)); return; } Scheduler.Worker w = scheduler.createWorker(); if (timespan == timeskip) { - source.subscribe(new BufferExactBoundedObserver( - new SerializedObserver(t), + source.subscribe(new BufferExactBoundedObserver<>( + new SerializedObserver<>(t), bufferSupplier, timespan, unit, maxSize, restartTimerOnMaxSize, w )); @@ -73,8 +72,8 @@ protected void subscribeActual(Observer t) { } // Can't use maxSize because what to do if a buffer is full but its // timespan hasn't been elapsed? - source.subscribe(new BufferSkipBoundedObserver( - new SerializedObserver(t), + source.subscribe(new BufferSkipBoundedObserver<>( + new SerializedObserver<>(t), bufferSupplier, timespan, timeskip, unit, w)); } @@ -90,12 +89,12 @@ static final class BufferExactUnboundedObserver timer = new AtomicReference(); + final AtomicReference timer = new AtomicReference<>(); BufferExactUnboundedObserver( Observer actual, Supplier bufferSupplier, long timespan, TimeUnit unit, Scheduler scheduler) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.timespan = timespan; this.unit = unit; @@ -110,7 +109,7 @@ public void onSubscribe(Disposable d) { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); dispose(); @@ -122,11 +121,9 @@ public void onSubscribe(Disposable d) { downstream.onSubscribe(this); - if (!cancelled) { + if (!DisposableHelper.isDisposed(timer.get())) { Disposable task = scheduler.schedulePeriodicallyDirect(this, timespan, timespan, unit); - if (!timer.compareAndSet(null, task)) { - task.dispose(); - } + DisposableHelper.set(timer, task); } } } @@ -184,7 +181,7 @@ public void run() { U next; try { - next = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); + next = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); @@ -229,13 +226,13 @@ static final class BufferSkipBoundedObserver> BufferSkipBoundedObserver(Observer actual, Supplier bufferSupplier, long timespan, long timeskip, TimeUnit unit, Worker w) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.timespan = timespan; this.timeskip = timeskip; this.unit = unit; this.w = w; - this.buffers = new LinkedList(); + this.buffers = new LinkedList<>(); } @Override @@ -246,7 +243,7 @@ public void onSubscribe(Disposable d) { final U b; // NOPMD try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); d.dispose(); @@ -286,7 +283,7 @@ public void onError(Throwable t) { public void onComplete() { List bs; synchronized (this) { - bs = new ArrayList(buffers); + bs = new ArrayList<>(buffers); buffers.clear(); } @@ -328,7 +325,7 @@ public void run() { final U b; // NOPMD try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); + b = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); @@ -410,7 +407,7 @@ static final class BufferExactBoundedObserver Supplier bufferSupplier, long timespan, TimeUnit unit, int maxSize, boolean restartOnMaxSize, Worker w) { - super(actual, new MpscLinkedQueue()); + super(actual, new MpscLinkedQueue<>()); this.bufferSupplier = bufferSupplier; this.timespan = timespan; this.unit = unit; @@ -427,7 +424,7 @@ public void onSubscribe(Disposable d) { U b; try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); d.dispose(); @@ -469,7 +466,7 @@ public void onNext(T t) { fastPathOrderedEmit(b, false, this); try { - b = ObjectHelper.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); + b = Objects.requireNonNull(bufferSupplier.get(), "The buffer supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); @@ -541,7 +538,7 @@ public void run() { U next; try { - next = ObjectHelper.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); + next = Objects.requireNonNull(bufferSupplier.get(), "The bufferSupplier returned a null buffer"); } catch (Throwable e) { Exceptions.throwIfFatal(e); dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCache.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCache.java index d03ad2008e..99e11259a6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCache.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCache.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,23 +24,7 @@ * * @param the source element type */ -public final class ObservableCache extends AbstractObservableWithUpstream -implements Observer { - - /** - * The subscription to the source should happen at most once. - */ - final AtomicBoolean once; - - /** - * The number of items per cached nodes. - */ - final int capacityHint; - - /** - * The current known array of observer state to notify. - */ - final AtomicReference[]> observers; +public final class ObservableCache extends AbstractObservableWithUpstream { /** * A shared instance of an empty array of observers to avoid creating @@ -56,61 +40,49 @@ public final class ObservableCache extends AbstractObservableWithUpstream head; - - /** - * The current tail of the linked structure holding the items. - */ - Node tail; - - /** - * How many items have been put into the tail node so far. + * The subscription to the source should happen at most once. */ - int tailOffset; + final AtomicBoolean once; /** - * If {@link #observers} is {@link #TERMINATED}, this holds the terminal error if not null. + * Responsible caching events from the source and multicasting them to each downstream. */ - Throwable error; + final Multicaster multicaster; /** - * True if the source has terminated. + * The first node in a singly linked list. Each node has the capacity to hold a specific number of events, and each + * points exclusively to the next node (if present). When a new downstream arrives, the subscription is + * initialized with a reference to the "head" node, and any events present in the linked list are replayed. As + * events are replayed to the new downstream, its 'node' reference advances through the linked list, discarding each + * node reference once all events in that node have been replayed. Consequently, once {@code this} instance goes out + * of scope, the prefix of nodes up to the first node that is still being replayed becomes unreachable and eligible + * for collection. */ - volatile boolean done; + final Node head; /** * Constructs an empty, non-connected cache. * @param source the source to subscribe to for the first incoming observer * @param capacityHint the number of items expected (reduce allocation frequency) */ - @SuppressWarnings("unchecked") public ObservableCache(Observable source, int capacityHint) { super(source); - this.capacityHint = capacityHint; this.once = new AtomicBoolean(); - Node n = new Node(capacityHint); + Node n = new Node<>(capacityHint); this.head = n; - this.tail = n; - this.observers = new AtomicReference[]>(EMPTY); + this.multicaster = new Multicaster<>(capacityHint, n); } @Override protected void subscribeActual(Observer t) { - CacheDisposable consumer = new CacheDisposable(t, this); + CacheDisposable consumer = new CacheDisposable<>(t, multicaster, head); t.onSubscribe(consumer); - add(consumer); + multicaster.add(consumer); if (!once.get() && once.compareAndSet(false, true)) { - source.subscribe(this); + source.subscribe(multicaster); } else { - replay(consumer); + multicaster.replay(consumer); } } @@ -127,7 +99,7 @@ protected void subscribeActual(Observer t) { * @return true if the cache has observers */ /* public */ boolean hasObservers() { - return observers.get().length != 0; + return multicaster.get().length != 0; } /** @@ -135,194 +107,241 @@ protected void subscribeActual(Observer t) { * @return the number of currently cached event count */ /* public */ long cachedEventCount() { - return size; + return multicaster.size; } - /** - * Atomically adds the consumer to the {@link #observers} copy-on-write array - * if the source has not yet terminated. - * @param consumer the consumer to add - */ - void add(CacheDisposable consumer) { - for (;;) { - CacheDisposable[] current = observers.get(); - if (current == TERMINATED) { - return; - } - int n = current.length; + static final class Multicaster extends AtomicReference[]> implements Observer { - @SuppressWarnings("unchecked") - CacheDisposable[] next = new CacheDisposable[n + 1]; - System.arraycopy(current, 0, next, 0, n); - next[n] = consumer; + /** */ + private static final long serialVersionUID = 8514643269016498691L; - if (observers.compareAndSet(current, next)) { - return; - } - } - } + /** + * The number of items per cached nodes. + */ + final int capacityHint; - /** - * Atomically removes the consumer from the {@link #observers} copy-on-write array. - * @param consumer the consumer to remove - */ - @SuppressWarnings("unchecked") - void remove(CacheDisposable consumer) { - for (;;) { - CacheDisposable[] current = observers.get(); - int n = current.length; - if (n == 0) { - return; - } + /** + * The total number of elements in the list available for reads. + */ + volatile long size; - int j = -1; - for (int i = 0; i < n; i++) { - if (current[i] == consumer) { - j = i; - break; - } - } + /** + * The current tail of the linked structure holding the items. + */ + Node tail; - if (j < 0) { - return; - } - CacheDisposable[] next; + /** + * How many items have been put into the tail node so far. + */ + int tailOffset; - if (n == 1) { - next = EMPTY; - } else { - next = new CacheDisposable[n - 1]; - System.arraycopy(current, 0, next, 0, j); - System.arraycopy(current, j + 1, next, j, n - j - 1); - } + /** + * If the observers are {@link #TERMINATED}, this holds the terminal error if not null. + */ + Throwable error; - if (observers.compareAndSet(current, next)) { - return; - } - } - } + /** + * True if the source has terminated. + */ + volatile boolean done; - /** - * Replays the contents of this cache to the given consumer based on its - * current state and number of items requested by it. - * @param consumer the consumer to continue replaying items to - */ - void replay(CacheDisposable consumer) { - // make sure there is only one replay going on at a time - if (consumer.getAndIncrement() != 0) { - return; + @SuppressWarnings("unchecked") + Multicaster(int capacityHint, final Node head) { + super(EMPTY); + this.tail = head; + this.capacityHint = capacityHint; } - // see if there were more replay request in the meantime - int missed = 1; - // read out state into locals upfront to avoid being re-read due to volatile reads - long index = consumer.index; - int offset = consumer.offset; - Node node = consumer.node; - Observer downstream = consumer.downstream; - int capacity = capacityHint; - - for (;;) { - // if the consumer got disposed, clear the node and quit - if (consumer.disposed) { - consumer.node = null; - return; + /** + * Atomically adds the consumer to the observers copy-on-write array + * if the source has not yet terminated. + * @param consumer the consumer to add + */ + void add(CacheDisposable consumer) { + for (;;) { + CacheDisposable[] current = get(); + if (current == TERMINATED) { + return; + } + int n = current.length; + + @SuppressWarnings("unchecked") + CacheDisposable[] next = new CacheDisposable[n + 1]; + System.arraycopy(current, 0, next, 0, n); + next[n] = consumer; + + if (compareAndSet(current, next)) { + return; + } } + } - // first see if the source has terminated, read order matters! - boolean sourceDone = done; - // and if the number of items is the same as this consumer has received - boolean empty = size == index; - - // if the source is done and we have all items so far, terminate the consumer - if (sourceDone && empty) { - // release the node object to avoid leaks through retained consumers - consumer.node = null; - // if error is not null then the source failed - Throwable ex = error; - if (ex != null) { - downstream.onError(ex); + /** + * Atomically removes the consumer from the observers copy-on-write array. + * @param consumer the consumer to remove + */ + @SuppressWarnings("unchecked") + void remove(CacheDisposable consumer) { + for (;;) { + CacheDisposable[] current = get(); + int n = current.length; + if (n == 0) { + return; + } + + int j = -1; + for (int i = 0; i < n; i++) { + if (current[i] == consumer) { + j = i; + break; + } + } + + if (j < 0) { + return; + } + CacheDisposable[] next; + + if (n == 1) { + next = EMPTY; } else { - downstream.onComplete(); + next = new CacheDisposable[n - 1]; + System.arraycopy(current, 0, next, 0, j); + System.arraycopy(current, j + 1, next, j, n - j - 1); } + + if (compareAndSet(current, next)) { + return; + } + } + } + + /** + * Replays the contents of this cache to the given consumer based on its + * current state and number of items requested by it. + * @param consumer the consumer to continue replaying items to + */ + void replay(CacheDisposable consumer) { + // make sure there is only one replay going on at a time + if (consumer.getAndIncrement() != 0) { return; } - // there are still items not sent to the consumer - if (!empty) { - // if the offset in the current node has reached the node capacity - if (offset == capacity) { - // switch to the subsequent node - node = node.next; - // reset the in-node offset - offset = 0; + // see if there were more replay request in the meantime + int missed = 1; + // read out state into locals upfront to avoid being re-read due to volatile reads + long index = consumer.index; + int offset = consumer.offset; + Node node = consumer.node; + Observer downstream = consumer.downstream; + int capacity = capacityHint; + + for (;;) { + // if the consumer got disposed, clear the node and quit + if (consumer.disposed) { + consumer.node = null; + return; } - // emit the cached item - downstream.onNext(node.values[offset]); - - // move the node offset forward - offset++; - // move the total consumed item count forward - index++; + // first see if the source has terminated, read order matters! + boolean sourceDone = done; + // and if the number of items is the same as this consumer has received + boolean empty = size == index; + + // if the source is done and we have all items so far, terminate the consumer + if (sourceDone && empty) { + // release the node object to avoid leaks through retained consumers + consumer.node = null; + // if error is not null then the source failed + Throwable ex = error; + if (ex != null) { + downstream.onError(ex); + } else { + downstream.onComplete(); + } + return; + } - // retry for the next item/terminal event if any - continue; - } + // there are still items not sent to the consumer + if (!empty) { + // if the offset in the current node has reached the node capacity + if (offset == capacity) { + // switch to the subsequent node + node = node.next; + // reset the in-node offset + offset = 0; + } + + // emit the cached item + downstream.onNext(node.values[offset]); + + // move the node offset forward + offset++; + // move the total consumed item count forward + index++; + + // retry for the next item/terminal event if any + continue; + } - // commit the changed references back - consumer.index = index; - consumer.offset = offset; - consumer.node = node; - // release the changes and see if there were more replay request in the meantime - missed = consumer.addAndGet(-missed); - if (missed == 0) { - break; + // commit the changed references back + consumer.index = index; + consumer.offset = offset; + consumer.node = node; + // release the changes and see if there were more replay request in the meantime + missed = consumer.addAndGet(-missed); + if (missed == 0) { + break; + } } } - } - @Override - public void onSubscribe(Disposable d) { - // we can't do much with the upstream disposable - } - - @Override - public void onNext(T t) { - int tailOffset = this.tailOffset; - // if the current tail node is full, create a fresh node - if (tailOffset == capacityHint) { - Node n = new Node(tailOffset); - n.values[0] = t; - this.tailOffset = 1; - tail.next = n; - tail = n; - } else { - tail.values[tailOffset] = t; - this.tailOffset = tailOffset + 1; + @Override + public void onSubscribe(Disposable d) { + // we can't do much with the upstream disposable } - size++; - for (CacheDisposable consumer : observers.get()) { - replay(consumer); + + @Override + public void onNext(T t) { + int tailOffset = this.tailOffset; + // if the current tail node is full, create a fresh node + if (tailOffset == capacityHint) { + Node n = new Node<>(tailOffset); + n.values[0] = t; + this.tailOffset = 1; + tail.next = n; + tail = n; + } else { + tail.values[tailOffset] = t; + this.tailOffset = tailOffset + 1; + } + size++; + for (CacheDisposable consumer : get()) { + replay(consumer); + } } - } - @SuppressWarnings("unchecked") - @Override - public void onError(Throwable t) { - error = t; - done = true; - for (CacheDisposable consumer : observers.getAndSet(TERMINATED)) { - replay(consumer); + @SuppressWarnings("unchecked") + @Override + public void onError(Throwable t) { + error = t; + done = true; + // No additional events will arrive, so now we can clear the 'tail' reference + tail = null; + for (CacheDisposable consumer : getAndSet(TERMINATED)) { + replay(consumer); + } } - } - @SuppressWarnings("unchecked") - @Override - public void onComplete() { - done = true; - for (CacheDisposable consumer : observers.getAndSet(TERMINATED)) { - replay(consumer); + @SuppressWarnings("unchecked") + @Override + public void onComplete() { + done = true; + // No additional events will arrive, so now we can clear the 'tail' reference + tail = null; + for (CacheDisposable consumer : getAndSet(TERMINATED)) { + replay(consumer); + } } } @@ -338,7 +357,7 @@ static final class CacheDisposable extends AtomicInteger final Observer downstream; - final ObservableCache parent; + final Multicaster parent; Node node; @@ -353,11 +372,12 @@ static final class CacheDisposable extends AtomicInteger * the parent cache object. * @param downstream the actual consumer * @param parent the parent that holds onto the cached items + * @param head the first node in the linked list */ - CacheDisposable(Observer downstream, ObservableCache parent) { + CacheDisposable(Observer downstream, Multicaster parent, Node head) { this.downstream = downstream; this.parent = parent; - this.node = parent.head; + this.node = head; } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollect.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollect.java index 3b81a32ad9..6b700addd7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollect.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollect.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,15 +10,18 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class ObservableCollect extends AbstractObservableWithUpstream { final Supplier initialSupplier; final BiConsumer collector; @@ -34,13 +37,14 @@ public ObservableCollect(ObservableSource source, protected void subscribeActual(Observer t) { U u; try { - u = ObjectHelper.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); + u = Objects.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); } catch (Throwable e) { + Exceptions.throwIfFatal(e); EmptyDisposable.error(e, t); return; } - source.subscribe(new CollectObserver(t, u, collector)); + source.subscribe(new CollectObserver<>(t, u, collector)); } @@ -85,6 +89,7 @@ public void onNext(T t) { try { collector.accept(u, t); } catch (Throwable e) { + Exceptions.throwIfFatal(e); upstream.dispose(); onError(e); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectSingle.java index a63812a9ab..88137432ac 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,16 +10,19 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.FuseToObservable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class ObservableCollectSingle extends Single implements FuseToObservable { final ObservableSource source; @@ -38,18 +41,19 @@ public ObservableCollectSingle(ObservableSource source, protected void subscribeActual(SingleObserver t) { U u; try { - u = ObjectHelper.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); + u = Objects.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); } catch (Throwable e) { + Exceptions.throwIfFatal(e); EmptyDisposable.error(e, t); return; } - source.subscribe(new CollectObserver(t, u, collector)); + source.subscribe(new CollectObserver<>(t, u, collector)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableCollect(source, initialSupplier, collector)); + return RxJavaPlugins.onAssembly(new ObservableCollect<>(source, initialSupplier, collector)); } static final class CollectObserver implements Observer, Disposable { @@ -93,6 +97,7 @@ public void onNext(T t) { try { collector.accept(u, t); } catch (Throwable e) { + Exceptions.throwIfFatal(e); upstream.dispose(); onError(e); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java index 6a379d142c..fded0d0ef5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,9 +21,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableCombineLatest extends Observable { final ObservableSource[] sources; @@ -49,13 +49,19 @@ public void subscribeActual(Observer observer) { int count = 0; if (sources == null) { sources = new ObservableSource[8]; - for (ObservableSource p : sourcesIterable) { - if (count == sources.length) { - ObservableSource[] b = new ObservableSource[count + (count >> 2)]; - System.arraycopy(sources, 0, b, 0, count); - sources = b; + try { + for (ObservableSource p : sourcesIterable) { + if (count == sources.length) { + ObservableSource[] b = new ObservableSource[count + (count >> 2)]; + System.arraycopy(sources, 0, b, 0, count); + sources = b; + } + sources[count++] = Objects.requireNonNull(p, "The Iterator returned a null ObservableSource"); } - sources[count++] = p; + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + EmptyDisposable.error(ex, observer); + return; } } else { count = sources.length; @@ -66,7 +72,7 @@ public void subscribeActual(Observer observer) { return; } - LatestCoordinator lc = new LatestCoordinator(observer, combiner, count, bufferSize, delayError); + LatestCoordinator lc = new LatestCoordinator<>(observer, combiner, count, bufferSize, delayError); lc.subscribe(sources); } @@ -99,10 +105,10 @@ static final class LatestCoordinator extends AtomicInteger implements Disp this.latest = new Object[count]; CombinerObserver[] as = new CombinerObserver[count]; for (int i = 0; i < count; i++) { - as[i] = new CombinerObserver(this, i); + as[i] = new CombinerObserver<>(this, i); } this.observers = as; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); } public void subscribe(ObservableSource[] sources) { @@ -122,9 +128,7 @@ public void dispose() { if (!cancelled) { cancelled = true; cancelSources(); - if (getAndIncrement() == 0) { - clear(queue); - } + drain(); } } @@ -161,6 +165,7 @@ void drain() { for (;;) { if (cancelled) { clear(q); + errors.tryTerminateAndReport(); return; } @@ -188,7 +193,7 @@ void drain() { R v; try { - v = ObjectHelper.requireNonNull(combiner.apply(s), "The combiner returned a null value"); + v = Objects.requireNonNull(combiner.apply(s), "The combiner returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); errors.tryAddThrowableOrReport(ex); @@ -240,7 +245,6 @@ void innerError(int index, Throwable ex) { if (latest == null) { return; } - cancelOthers = latest[index] == null; if (cancelOthers || ++complete == latest.length) { done = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap.java index 665306db95..b8f34df3d7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,10 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -19,11 +21,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.observers.SerializedObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableConcatMap extends AbstractObservableWithUpstream { @@ -48,10 +50,10 @@ public void subscribeActual(Observer observer) { } if (delayErrors == ErrorMode.IMMEDIATE) { - SerializedObserver serial = new SerializedObserver(observer); - source.subscribe(new SourceObserver(serial, mapper, bufferSize)); + SerializedObserver serial = new SerializedObserver<>(observer); + source.subscribe(new SourceObserver<>(serial, mapper, bufferSize)); } else { - source.subscribe(new ConcatMapDelayErrorObserver(observer, mapper, bufferSize, delayErrors == ErrorMode.END)); + source.subscribe(new ConcatMapDelayErrorObserver<>(observer, mapper, bufferSize, delayErrors == ErrorMode.END)); } } @@ -80,7 +82,7 @@ static final class SourceObserver extends AtomicInteger implements Observe this.downstream = actual; this.mapper = mapper; this.bufferSize = bufferSize; - this.inner = new InnerObserver(actual, this); + this.inner = new InnerObserver<>(actual, this); } @Override @@ -113,7 +115,7 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); downstream.onSubscribe(this); } @@ -208,7 +210,7 @@ void drain() { ObservableSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); + o = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); dispose(); @@ -305,7 +307,7 @@ static final class ConcatMapDelayErrorObserver this.bufferSize = bufferSize; this.tillTheEnd = tillTheEnd; this.errors = new AtomicThrowable(); - this.observer = new DelayErrorInnerObserver(actual, this); + this.observer = new DelayErrorInnerObserver<>(actual, this); } @Override @@ -338,7 +340,7 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); downstream.onSubscribe(this); } @@ -436,7 +438,7 @@ void drain() { ObservableSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null ObservableSource"); + o = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancelled = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEager.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEager.java index 47c4099b83..5a4b1c3a47 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEager.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEager.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.ArrayDeque; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.rxjava3.core.*; @@ -21,11 +22,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.observers.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableConcatMapEager extends AbstractObservableWithUpstream { @@ -50,7 +51,7 @@ public ObservableConcatMapEager(ObservableSource source, @Override protected void subscribeActual(Observer observer) { - source.subscribe(new ConcatMapEagerMainObserver(observer, mapper, maxConcurrency, prefetch, errorMode)); + source.subscribe(new ConcatMapEagerMainObserver<>(observer, mapper, maxConcurrency, prefetch, errorMode)); } static final class ConcatMapEagerMainObserver @@ -96,7 +97,7 @@ static final class ConcatMapEagerMainObserver this.prefetch = prefetch; this.errorMode = errorMode; this.errors = new AtomicThrowable(); - this.observers = new ArrayDeque>(); + this.observers = new ArrayDeque<>(); } @SuppressWarnings("unchecked") @@ -129,7 +130,7 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(prefetch); + queue = new SpscLinkedArrayQueue<>(prefetch); downstream.onSubscribe(this); } @@ -271,7 +272,7 @@ public void drain() { break; } - source = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null ObservableSource"); + source = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -282,7 +283,7 @@ public void drain() { return; } - InnerQueuedObserver inner = new InnerQueuedObserver(this, prefetch); + InnerQueuedObserver inner = new InnerQueuedObserver<>(this, prefetch); observers.offer(inner); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapScheduler.java index efe4b79fc6..1ea59c83c3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,10 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -19,11 +21,11 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.observers.SerializedObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableConcatMapScheduler extends AbstractObservableWithUpstream { @@ -48,10 +50,10 @@ public ObservableConcatMapScheduler(ObservableSource source, Function observer) { if (delayErrors == ErrorMode.IMMEDIATE) { - SerializedObserver serial = new SerializedObserver(observer); - source.subscribe(new ConcatMapObserver(serial, mapper, bufferSize, scheduler.createWorker())); + SerializedObserver serial = new SerializedObserver<>(observer); + source.subscribe(new ConcatMapObserver<>(serial, mapper, bufferSize, scheduler.createWorker())); } else { - source.subscribe(new ConcatMapDelayErrorObserver(observer, mapper, bufferSize, delayErrors == ErrorMode.END, scheduler.createWorker())); + source.subscribe(new ConcatMapDelayErrorObserver<>(observer, mapper, bufferSize, delayErrors == ErrorMode.END, scheduler.createWorker())); } } @@ -81,7 +83,7 @@ static final class ConcatMapObserver extends AtomicInteger implements Obse this.downstream = actual; this.mapper = mapper; this.bufferSize = bufferSize; - this.inner = new InnerObserver(actual, this); + this.inner = new InnerObserver<>(actual, this); this.worker = worker; } @@ -115,7 +117,7 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); downstream.onSubscribe(this); } @@ -218,7 +220,7 @@ public void run() { ObservableSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); + o = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); dispose(); @@ -318,7 +320,7 @@ static final class ConcatMapDelayErrorObserver this.bufferSize = bufferSize; this.tillTheEnd = tillTheEnd; this.errors = new AtomicThrowable(); - this.observer = new DelayErrorInnerObserver(actual, this); + this.observer = new DelayErrorInnerObserver<>(actual, this); this.worker = worker; } @@ -352,7 +354,7 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); downstream.onSubscribe(this); } @@ -459,7 +461,7 @@ public void run() { ObservableSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(v), "The mapper returned a null ObservableSource"); + o = Objects.requireNonNull(mapper.apply(v), "The mapper returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancelled = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletable.java index 333428a80b..8d97074a8c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public ObservableConcatWithCompletable(Observable source, CompletableSource o @Override protected void subscribeActual(Observer observer) { - source.subscribe(new ConcatWithObserver(observer, other)); + source.subscribe(new ConcatWithObserver<>(observer, other)); } static final class ConcatWithObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybe.java index 7f3d8d8716..e198a0412a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public ObservableConcatWithMaybe(Observable source, MaybeSource @Override protected void subscribeActual(Observer observer) { - source.subscribe(new ConcatWithObserver(observer, other)); + source.subscribe(new ConcatWithObserver<>(observer, other)); } static final class ConcatWithObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingle.java index 3c91beb30f..3d69ae83ac 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public ObservableConcatWithSingle(Observable source, SingleSource observer) { - source.subscribe(new ConcatWithObserver(observer, other)); + source.subscribe(new ConcatWithObserver<>(observer, other)); } static final class ConcatWithObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCount.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCount.java index f7ab3b012f..366c4bb3db 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCount.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCount.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountSingle.java index 84042bfbea..be73420542 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public void subscribeActual(SingleObserver t) { @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableCount(source)); + return RxJavaPlugins.onAssembly(new ObservableCount<>(source)); } static final class CountObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java index fe1f394ff3..1e2e910f0f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import java.util.concurrent.atomic.*; @@ -19,9 +20,9 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Cancellable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableCreate extends Observable { @@ -33,7 +34,7 @@ public ObservableCreate(ObservableOnSubscribe source) { @Override protected void subscribeActual(Observer observer) { - CreateEmitter parent = new CreateEmitter(observer); + CreateEmitter parent = new CreateEmitter<>(observer); observer.onSubscribe(parent); try { @@ -113,7 +114,7 @@ public void setCancellable(Cancellable c) { @Override public ObservableEmitter serialize() { - return new SerializedEmitter(this); + return new SerializedEmitter<>(this); } @Override @@ -154,12 +155,12 @@ static final class SerializedEmitter SerializedEmitter(ObservableEmitter emitter) { this.emitter = emitter; this.errors = new AtomicThrowable(); - this.queue = new SpscLinkedArrayQueue(16); + this.queue = new SpscLinkedArrayQueue<>(16); } @Override public void onNext(T t) { - if (emitter.isDisposed() || done) { + if (done || emitter.isDisposed()) { return; } if (t == null) { @@ -192,7 +193,7 @@ public void onError(Throwable t) { @Override public boolean tryOnError(Throwable t) { - if (emitter.isDisposed() || done) { + if (done || emitter.isDisposed()) { return false; } if (t == null) { @@ -208,7 +209,7 @@ public boolean tryOnError(Throwable t) { @Override public void onComplete() { - if (emitter.isDisposed() || done) { + if (done || emitter.isDisposed()) { return; } done = true; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounce.java index d2e1f8cf18..862227e305 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounce.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounce.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -34,7 +34,7 @@ public ObservableDebounce(ObservableSource source, Function t) { - source.subscribe(new DebounceObserver(new SerializedObserver(t), debounceSelector)); + source.subscribe(new DebounceObserver<>(new SerializedObserver<>(t), debounceSelector)); } static final class DebounceObserver @@ -44,7 +44,7 @@ static final class DebounceObserver Disposable upstream; - final AtomicReference debouncer = new AtomicReference(); + final AtomicReference debouncer = new AtomicReference<>(); volatile long index; @@ -81,7 +81,7 @@ public void onNext(T t) { ObservableSource p; try { - p = ObjectHelper.requireNonNull(debounceSelector.apply(t), "The ObservableSource supplied is null"); + p = Objects.requireNonNull(debounceSelector.apply(t), "The ObservableSource supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); dispose(); @@ -89,7 +89,7 @@ public void onNext(T t) { return; } - DebounceInnerObserver dis = new DebounceInnerObserver(this, idx, t); + DebounceInnerObserver dis = new DebounceInnerObserver<>(this, idx, t); if (debouncer.compareAndSet(d, dis)) { p.subscribe(dis); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTimed.java index 626fe77928..f2db191229 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,6 +19,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; import io.reactivex.rxjava3.observers.SerializedObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -27,19 +29,20 @@ public final class ObservableDebounceTimed extends AbstractObservableWithUpst final long timeout; final TimeUnit unit; final Scheduler scheduler; + final Consumer onDropped; - public ObservableDebounceTimed(ObservableSource source, long timeout, TimeUnit unit, Scheduler scheduler) { + public ObservableDebounceTimed(ObservableSource source, long timeout, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { super(source); this.timeout = timeout; this.unit = unit; this.scheduler = scheduler; + this.onDropped = onDropped; } @Override public void subscribeActual(Observer t) { - source.subscribe(new DebounceTimedObserver( - new SerializedObserver(t), - timeout, unit, scheduler.createWorker())); + source.subscribe(new DebounceTimedObserver<>( + new SerializedObserver<>(t), timeout, unit, scheduler.createWorker(), onDropped)); } static final class DebounceTimedObserver @@ -48,20 +51,22 @@ static final class DebounceTimedObserver final long timeout; final TimeUnit unit; final Scheduler.Worker worker; + final Consumer onDropped; Disposable upstream; - Disposable timer; + DebounceEmitter timer; volatile long index; boolean done; - DebounceTimedObserver(Observer actual, long timeout, TimeUnit unit, Worker worker) { + DebounceTimedObserver(Observer actual, long timeout, TimeUnit unit, Worker worker, Consumer onDropped) { this.downstream = actual; this.timeout = timeout; this.unit = unit; this.worker = worker; + this.onDropped = onDropped; } @Override @@ -80,15 +85,25 @@ public void onNext(T t) { long idx = index + 1; index = idx; - Disposable d = timer; - if (d != null) { - d.dispose(); + DebounceEmitter currentEmitter = timer; + if (currentEmitter != null) { + currentEmitter.dispose(); } - DebounceEmitter de = new DebounceEmitter(t, idx, this); - timer = de; - d = worker.schedule(de, timeout, unit); - de.setResource(d); + if (onDropped != null && currentEmitter != null) { + try { + onDropped.accept(timer.value); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + downstream.onError(ex); + done = true; + } + } + + DebounceEmitter newEmitter = new DebounceEmitter<>(t, idx, this); + timer = newEmitter; + newEmitter.setResource(worker.schedule(newEmitter, timeout, unit)); } @Override @@ -113,15 +128,13 @@ public void onComplete() { } done = true; - Disposable d = timer; + DebounceEmitter d = timer; if (d != null) { d.dispose(); } - @SuppressWarnings("unchecked") - DebounceEmitter de = (DebounceEmitter)d; - if (de != null) { - de.run(); + if (d != null) { + d.run(); } downstream.onComplete(); worker.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefer.java index c1f8dc41ab..0b3388c84c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class ObservableDefer extends Observable { final Supplier> supplier; @@ -29,7 +30,7 @@ public ObservableDefer(Supplier> supplie public void subscribeActual(Observer observer) { ObservableSource pub; try { - pub = ObjectHelper.requireNonNull(supplier.get(), "The supplier returned a null ObservableSource"); + pub = Objects.requireNonNull(supplier.get(), "The supplier returned a null ObservableSource"); } catch (Throwable t) { Exceptions.throwIfFatal(t); EmptyDisposable.error(t, observer); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelay.java index 61e424a3eb..7c01c23f90 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,12 +42,12 @@ public void subscribeActual(Observer t) { if (delayError) { observer = (Observer)t; } else { - observer = new SerializedObserver(t); + observer = new SerializedObserver<>(t); } Scheduler.Worker w = scheduler.createWorker(); - source.subscribe(new DelayObserver(observer, delay, unit, w, delayError)); + source.subscribe(new DelayObserver<>(observer, delay, unit, w, delayError)); } static final class DelayObserver implements Observer, Disposable { @@ -111,7 +111,9 @@ final class OnNext implements Runnable { @Override public void run() { - downstream.onNext(t); + if (!w.isDisposed()) { + downstream.onNext(t); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOther.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOther.java index c594c68b00..978e4d2f32 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOther.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOther.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerialize.java index 59da21cd47..2016451986 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class ObservableDematerialize extends AbstractObservableWithUpstream { final Function> selector; @@ -32,7 +33,7 @@ public ObservableDematerialize(ObservableSource source, Function observer) { - source.subscribe(new DematerializeObserver(observer, selector)); + source.subscribe(new DematerializeObserver<>(observer, selector)); } static final class DematerializeObserver implements Observer, Disposable { @@ -83,7 +84,7 @@ public void onNext(T item) { Notification notification; try { - notification = ObjectHelper.requireNonNull(selector.apply(item), "The selector returned a null Notification"); + notification = Objects.requireNonNull(selector.apply(item), "The selector returned a null Notification"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetach.java index 7665a6b4f8..b9c6f2bade 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public ObservableDetach(ObservableSource source) { @Override protected void subscribeActual(Observer observer) { - source.subscribe(new DetachObserver(observer)); + source.subscribe(new DetachObserver<>(observer)); } static final class DetachObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinct.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinct.java index 98c197156c..ff377833a5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinct.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinct.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.Collection; +import java.util.Objects; import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicFuseableObserver; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -49,7 +49,7 @@ protected void subscribeActual(Observer observer) { return; } - source.subscribe(new DistinctObserver(observer, keySelector, collection)); + source.subscribe(new DistinctObserver<>(observer, keySelector, collection)); } static final class DistinctObserver extends BasicFuseableObserver { @@ -74,7 +74,7 @@ public void onNext(T value) { boolean b; try { - key = ObjectHelper.requireNonNull(keySelector.apply(value), "The keySelector returned a null key"); + key = Objects.requireNonNull(keySelector.apply(value), "The keySelector returned a null key"); b = collection.add(key); } catch (Throwable ex) { fail(ex); @@ -120,7 +120,7 @@ public T poll() throws Throwable { for (;;) { T v = qd.poll(); - if (v == null || collection.add(ObjectHelper.requireNonNull(keySelector.apply(v), "The keySelector returned a null key"))) { + if (v == null || collection.add(Objects.requireNonNull(keySelector.apply(v), "The keySelector returned a null key"))) { return v; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChanged.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChanged.java index a36e92a3c4..7604db7c8f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChanged.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChanged.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public ObservableDistinctUntilChanged(ObservableSource source, Function observer) { - source.subscribe(new DistinctUntilChangedObserver(observer, keySelector, comparer)); + source.subscribe(new DistinctUntilChangedObserver<>(observer, keySelector, comparer)); } static final class DistinctUntilChangedObserver extends BasicFuseableObserver { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNext.java index 212b84fccc..6382e6b822 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public ObservableDoAfterNext(ObservableSource source, Consumer onA @Override protected void subscribeActual(Observer observer) { - source.subscribe(new DoAfterObserver(observer, onAfterNext)); + source.subscribe(new DoAfterObserver<>(observer, onAfterNext)); } static final class DoAfterObserver extends BasicFuseableObserver { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinally.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinally.java index e5087d0ee4..e6d9031f72 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinally.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinally.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,8 +19,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.QueueDisposable; import io.reactivex.rxjava3.internal.observers.BasicIntQueueDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -40,7 +40,7 @@ public ObservableDoFinally(ObservableSource source, Action onFinally) { @Override protected void subscribeActual(Observer observer) { - source.subscribe(new DoFinallyObserver(observer, onFinally)); + source.subscribe(new DoFinallyObserver<>(observer, onFinally)); } static final class DoFinallyObserver extends BasicIntQueueDisposable implements Observer { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEach.java index bbc34fed83..f4869d19a8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public ObservableDoOnEach(ObservableSource source, Consumer onNext @Override public void subscribeActual(Observer t) { - source.subscribe(new DoOnEachObserver(t, onNext, onError, onComplete, onAfterTerminate)); + source.subscribe(new DoOnEachObserver<>(t, onNext, onError, onComplete, onAfterTerminate)); } static final class DoOnEachObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnLifecycle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnLifecycle.java index b59cfdca64..f185231246 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnLifecycle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnLifecycle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -30,6 +31,6 @@ public ObservableDoOnLifecycle(Observable upstream, Consumer observer) { - source.subscribe(new DisposableLambdaObserver(observer, onSubscribe, onDispose)); + source.subscribe(new DisposableLambdaObserver<>(observer, onSubscribe, onDispose)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAt.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAt.java index 28ff883d03..8d8a535bcf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAt.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAt.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public ObservableElementAt(ObservableSource source, long index, T defaultValu @Override public void subscribeActual(Observer t) { - source.subscribe(new ElementAtObserver(t, index, defaultValue, errorOnFewer)); + source.subscribe(new ElementAtObserver<>(t, index, defaultValue, errorOnFewer)); } static final class ElementAtObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtMaybe.java index ad987cccd2..14bb368a23 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,12 +29,12 @@ public ObservableElementAtMaybe(ObservableSource source, long index) { @Override public void subscribeActual(MaybeObserver t) { - source.subscribe(new ElementAtObserver(t, index)); + source.subscribe(new ElementAtObserver<>(t, index)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableElementAt(source, index, null, false)); + return RxJavaPlugins.onAssembly(new ObservableElementAt<>(source, index, null, false)); } static final class ElementAtObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtSingle.java index 397e678265..c779f9eec2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,12 +34,12 @@ public ObservableElementAtSingle(ObservableSource source, long index, T defau @Override public void subscribeActual(SingleObserver t) { - source.subscribe(new ElementAtObserver(t, index, defaultValue)); + source.subscribe(new ElementAtObserver<>(t, index, defaultValue)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableElementAt(source, index, defaultValue, true)); + return RxJavaPlugins.onAssembly(new ObservableElementAt<>(source, index, defaultValue, true)); } static final class ElementAtObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableEmpty.java index d4869b70ea..7ff9e8b955 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,11 +10,12 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.operators.ScalarSupplier; public final class ObservableEmpty extends Observable implements ScalarSupplier { public static final Observable INSTANCE = new ObservableEmpty(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableError.java index 7481373e7a..f25af956e0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilter.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilter.java index 3bbacc6250..ed7068f6a0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilter.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public ObservableFilter(ObservableSource source, Predicate predica @Override public void subscribeActual(Observer observer) { - source.subscribe(new FilterObserver(observer, predicate)); + source.subscribe(new FilterObserver<>(observer, predicate)); } static final class FilterObserver extends BasicFuseableObserver { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java index 596719176e..d19b505999 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,10 +22,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.*; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableFlatMap extends AbstractObservableWithUpstream { @@ -51,7 +49,7 @@ public void subscribeActual(Observer t) { return; } - source.subscribe(new MergeObserver(t, mapper, delayErrors, maxConcurrency, bufferSize)); + source.subscribe(new MergeObserver<>(t, mapper, delayErrors, maxConcurrency, bufferSize)); } static final class MergeObserver extends AtomicInteger implements Disposable, Observer { @@ -70,7 +68,7 @@ static final class MergeObserver extends AtomicInteger implements Disposab final AtomicThrowable errors = new AtomicThrowable(); - volatile boolean cancelled; + volatile boolean disposed; final AtomicReference[]> observers; @@ -81,7 +79,6 @@ static final class MergeObserver extends AtomicInteger implements Disposab Disposable upstream; long uniqueId; - long lastId; int lastIndex; Queue> sources; @@ -96,9 +93,9 @@ static final class MergeObserver extends AtomicInteger implements Disposab this.maxConcurrency = maxConcurrency; this.bufferSize = bufferSize; if (maxConcurrency != Integer.MAX_VALUE) { - sources = new ArrayDeque>(maxConcurrency); + sources = new ArrayDeque<>(maxConcurrency); } - this.observers = new AtomicReference[]>(EMPTY); + this.observers = new AtomicReference<>(EMPTY); } @Override @@ -117,7 +114,7 @@ public void onNext(T t) { } ObservableSource p; try { - p = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); + p = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.dispose(); @@ -159,7 +156,7 @@ void subscribeInner(ObservableSource p) { break; } } else { - InnerObserver inner = new InnerObserver(this, uniqueId++); + InnerObserver inner = new InnerObserver<>(this, uniqueId++); if (addInner(inner)) { p.subscribe(inner); } @@ -189,9 +186,6 @@ void removeInner(InnerObserver inner) { for (;;) { InnerObserver[] a = observers.get(); int n = a.length; - if (n == 0) { - return; - } int j = -1; for (int i = 0; i < n; i++) { if (a[i] == inner) { @@ -240,17 +234,14 @@ boolean tryEmitScalar(Supplier value) { SimplePlainQueue q = queue; if (q == null) { if (maxConcurrency == Integer.MAX_VALUE) { - q = new SpscLinkedArrayQueue(bufferSize); + q = new SpscLinkedArrayQueue<>(bufferSize); } else { - q = new SpscArrayQueue(maxConcurrency); + q = new SpscArrayQueue<>(maxConcurrency); } queue = q; } - if (!q.offer(u)) { - onError(new IllegalStateException("Scalar queue full?!")); - return true; - } + q.offer(u); if (getAndIncrement() != 0) { return false; } @@ -268,7 +259,7 @@ void tryEmit(U value, InnerObserver inner) { } else { SimpleQueue q = inner.queue; if (q == null) { - q = new SpscLinkedArrayQueue(bufferSize); + q = new SpscLinkedArrayQueue<>(bufferSize); inner.queue = q; } q.offer(value); @@ -302,17 +293,15 @@ public void onComplete() { @Override public void dispose() { - if (!cancelled) { - cancelled = true; - if (disposeAll()) { - errors.tryTerminateAndReport(); - } + disposed = true; + if (disposeAll()) { + errors.tryTerminateAndReport(); } } @Override public boolean isDisposed() { - return cancelled; + return disposed; } void drain() { @@ -328,6 +317,7 @@ void drainLoop() { if (checkTerminate()) { return; } + int innerCompleted = 0; SimplePlainQueue svq = queue; if (svq != null) { @@ -343,9 +333,18 @@ void drainLoop() { } child.onNext(o); + innerCompleted++; } } + if (innerCompleted != 0) { + if (maxConcurrency != Integer.MAX_VALUE) { + subscribeMore(innerCompleted); + innerCompleted = 0; + } + continue; + } + boolean d = done; svq = queue; InnerObserver[] inner = observers.get(); @@ -363,31 +362,9 @@ void drainLoop() { return; } - int innerCompleted = 0; if (n != 0) { - long startId = lastId; - int index = lastIndex; - - if (n <= index || inner[index].id != startId) { - if (n <= index) { - index = 0; - } - int j = index; - for (int i = 0; i < n; i++) { - if (inner[j].id == startId) { - break; - } - j++; - if (j == n) { - j = 0; - } - } - index = j; - lastIndex = j; - lastId = inner[j].id; - } + int j = Math.min(n - 1, lastIndex); - int j = index; sourceLoop: for (int i = 0; i < n; i++) { if (checkTerminate()) { @@ -433,9 +410,6 @@ void drainLoop() { SimpleQueue innerQueue = is.queue; if (innerDone && (innerQueue == null || innerQueue.isEmpty())) { removeInner(is); - if (checkTerminate()) { - return; - } innerCompleted++; } @@ -445,25 +419,16 @@ void drainLoop() { } } lastIndex = j; - lastId = inner[j].id; } if (innerCompleted != 0) { if (maxConcurrency != Integer.MAX_VALUE) { - while (innerCompleted-- != 0) { - ObservableSource p; - synchronized (this) { - p = sources.poll(); - if (p == null) { - wip--; - continue; - } - } - subscribeInner(p); - } + subscribeMore(innerCompleted); + innerCompleted = 0; } continue; } + missed = addAndGet(-missed); if (missed == 0) { break; @@ -471,8 +436,22 @@ void drainLoop() { } } + void subscribeMore(int innerCompleted) { + while (innerCompleted-- != 0) { + ObservableSource p; + synchronized (this) { + p = sources.poll(); + if (p == null) { + wip--; + continue; + } + } + subscribeInner(p); + } + } + boolean checkTerminate() { - if (cancelled) { + if (disposed) { return true; } Throwable e = errors.get(); @@ -486,15 +465,12 @@ boolean checkTerminate() { boolean disposeAll() { upstream.dispose(); - InnerObserver[] a = observers.get(); + InnerObserver[] a = observers.getAndSet(CANCELLED); if (a != CANCELLED) { - a = observers.getAndSet(CANCELLED); - if (a != CANCELLED) { - for (InnerObserver inner : a) { - inner.dispose(); - } - return true; + for (InnerObserver inner : a) { + inner.dispose(); } + return true; } return false; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java index 26f0505a14..ae7c6f98ac 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.Nullable; @@ -21,7 +22,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicIntQueueDisposable; import io.reactivex.rxjava3.internal.util.AtomicThrowable; @@ -44,7 +44,7 @@ public ObservableFlatMapCompletable(ObservableSource source, @Override protected void subscribeActual(Observer observer) { - source.subscribe(new FlatMapCompletableMainObserver(observer, mapper, delayErrors)); + source.subscribe(new FlatMapCompletableMainObserver<>(observer, mapper, delayErrors)); } static final class FlatMapCompletableMainObserver extends BasicIntQueueDisposable @@ -88,7 +88,7 @@ public void onNext(T value) { CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -116,9 +116,7 @@ public void onError(Throwable e) { disposed = true; upstream.dispose(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } @@ -145,7 +143,7 @@ public boolean isDisposed() { @Nullable @Override - public T poll() throws Exception { + public T poll() { return null; // always empty } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java index b9a9213769..62ea03a54d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.FuseToObservable; import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -46,12 +46,12 @@ public ObservableFlatMapCompletableCompletable(ObservableSource source, @Override protected void subscribeActual(CompletableObserver observer) { - source.subscribe(new FlatMapCompletableMainObserver(observer, mapper, delayErrors)); + source.subscribe(new FlatMapCompletableMainObserver<>(observer, mapper, delayErrors)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableFlatMapCompletable(source, mapper, delayErrors)); + return RxJavaPlugins.onAssembly(new ObservableFlatMapCompletable<>(source, mapper, delayErrors)); } static final class FlatMapCompletableMainObserver extends AtomicInteger implements Disposable, Observer { @@ -94,7 +94,7 @@ public void onNext(T value) { CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -122,9 +122,7 @@ public void onError(Throwable e) { disposed = true; upstream.dispose(); set.dispose(); - if (getAndSet(0) > 0) { - errors.tryTerminateConsumer(downstream); - } + errors.tryTerminateConsumer(downstream); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java index 9e323ec346..b8d223d4ac 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,9 +21,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; /** * Maps upstream values into MaybeSources and merges their signals into one sequence. @@ -44,7 +44,7 @@ public ObservableFlatMapMaybe(ObservableSource source, Function observer) { - source.subscribe(new FlatMapMaybeObserver(observer, mapper, delayErrors)); + source.subscribe(new FlatMapMaybeObserver<>(observer, mapper, delayErrors)); } static final class FlatMapMaybeObserver @@ -79,7 +79,7 @@ static final class FlatMapMaybeObserver this.set = new CompositeDisposable(); this.errors = new AtomicThrowable(); this.active = new AtomicInteger(1); - this.queue = new AtomicReference>(); + this.queue = new AtomicReference<>(); } @Override @@ -96,7 +96,7 @@ public void onNext(T t) { MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -172,16 +172,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue(Observable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; + } + current = new SpscLinkedArrayQueue<>(Observable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java index 4ca080a73f..985e4fa2d9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,9 +21,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; /** * Maps upstream values into SingleSources and merges their signals into one sequence. @@ -44,7 +44,7 @@ public ObservableFlatMapSingle(ObservableSource source, Function observer) { - source.subscribe(new FlatMapSingleObserver(observer, mapper, delayErrors)); + source.subscribe(new FlatMapSingleObserver<>(observer, mapper, delayErrors)); } static final class FlatMapSingleObserver @@ -79,7 +79,7 @@ static final class FlatMapSingleObserver this.set = new CompositeDisposable(); this.errors = new AtomicThrowable(); this.active = new AtomicInteger(1); - this.queue = new AtomicReference>(); + this.queue = new AtomicReference<>(); } @Override @@ -96,7 +96,7 @@ public void onNext(T t) { SingleSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); + ms = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); @@ -172,16 +172,15 @@ void innerSuccess(InnerObserver inner, R value) { } SpscLinkedArrayQueue getOrCreateQueue() { - for (;;) { - SpscLinkedArrayQueue current = queue.get(); - if (current != null) { - return current; - } - current = new SpscLinkedArrayQueue(Observable.bufferSize()); - if (queue.compareAndSet(null, current)) { - return current; - } + SpscLinkedArrayQueue current = queue.get(); + if (current != null) { + return current; + } + current = new SpscLinkedArrayQueue<>(Observable.bufferSize()); + if (queue.compareAndSet(null, current)) { + return current; } + return queue.get(); } void innerError(InnerObserver inner, Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterable.java index c6704398a1..47dbde9a1e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.Iterator; +import java.util.Objects; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -41,7 +41,7 @@ public ObservableFlattenIterable(ObservableSource source, @Override protected void subscribeActual(Observer observer) { - source.subscribe(new FlattenIterableObserver(observer, mapper)); + source.subscribe(new FlattenIterableObserver<>(observer, mapper)); } static final class FlattenIterableObserver implements Observer, Disposable { @@ -100,7 +100,7 @@ public void onNext(T value) { R v; try { - v = ObjectHelper.requireNonNull(it.next(), "The iterator returned a null value"); + v = Objects.requireNonNull(it.next(), "The iterator returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromAction.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromAction.java new file mode 100644 index 0000000000..cd0970f0d0 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromAction.java @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.fuseable.CancellableQueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Executes an {@link Action} and signals its exception or completes normally. + * + * @param the value type + * @since 3.0.0 + */ +public final class ObservableFromAction extends Observable implements Supplier { + + final Action action; + + public ObservableFromAction(Action action) { + this.action = action; + } + + @Override + protected void subscribeActual(Observer observer) { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + observer.onSubscribe(qs); + + if (!qs.isDisposed()) { + + try { + action.run(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + if (!qs.isDisposed()) { + observer.onError(ex); + } else { + RxJavaPlugins.onError(ex); + } + return; + } + + if (!qs.isDisposed()) { + observer.onComplete(); + } + } + } + + @Override + public T get() throws Throwable { + action.run(); + return null; // considered as onComplete() + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromArray.java index 7c4b09ba62..f84c118346 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,9 +15,10 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicQueueDisposable; +import java.util.Objects; + public final class ObservableFromArray extends Observable { final T[] array; public ObservableFromArray(T[] array) { @@ -26,7 +27,7 @@ public ObservableFromArray(T[] array) { @Override public void subscribeActual(Observer observer) { - FromArrayDisposable d = new FromArrayDisposable(observer, array); + FromArrayDisposable d = new FromArrayDisposable<>(observer, array); observer.onSubscribe(d); @@ -70,7 +71,7 @@ public T poll() { T[] a = array; if (i != a.length) { index = i + 1; - return ObjectHelper.requireNonNull(a[i], "The array element is null"); + return Objects.requireNonNull(a[i], "The array element is null"); } return null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallable.java index db08487f81..ab4b7aa7b8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public ObservableFromCallable(Callable callable) { @Override public void subscribeActual(Observer observer) { - DeferredScalarDisposable d = new DeferredScalarDisposable(observer); + DeferredScalarDisposable d = new DeferredScalarDisposable<>(observer); observer.onSubscribe(d); if (d.isDisposed()) { return; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletable.java new file mode 100644 index 0000000000..83bf51f7e0 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletable.java @@ -0,0 +1,89 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.internal.fuseable.*; + +/** + * Wrap a Completable into an Observable. + * + * @param the value type + * @since 3.0.0 + */ +public final class ObservableFromCompletable extends Observable implements HasUpstreamCompletableSource { + + final CompletableSource source; + + public ObservableFromCompletable(CompletableSource source) { + this.source = source; + } + + @Override + public CompletableSource source() { + return source; + } + + @Override + protected void subscribeActual(Observer observer) { + source.subscribe(new FromCompletableObserver(observer)); + } + + public static final class FromCompletableObserver + extends AbstractEmptyQueueFuseable + implements CompletableObserver { + + final Observer downstream; + + Disposable upstream; + + public FromCompletableObserver(Observer downstream) { + this.downstream = downstream; + } + + @Override + public void dispose() { + upstream.dispose(); + upstream = DisposableHelper.DISPOSED; + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onComplete() { + upstream = DisposableHelper.DISPOSED; + downstream.onComplete(); + } + + @Override + public void onError(Throwable e) { + upstream = DisposableHelper.DISPOSED; + downstream.onError(e); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromFuture.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromFuture.java index aa9e7da0bb..d6f21ad133 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromFuture.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromFuture.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public ObservableFromFuture(Future future, long timeout, TimeUnit u @Override public void subscribeActual(Observer observer) { - DeferredScalarDisposable d = new DeferredScalarDisposable(observer); + DeferredScalarDisposable d = new DeferredScalarDisposable<>(observer); observer.onSubscribe(d); if (!d.isDisposed()) { T v; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterable.java index 91811b3c4a..d5f402d193 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,12 +14,12 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.Iterator; +import java.util.Objects; import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicQueueDisposable; public final class ObservableFromIterable extends Observable { @@ -51,7 +51,7 @@ public void subscribeActual(Observer observer) { return; } - FromIterableDisposable d = new FromIterableDisposable(observer, it); + FromIterableDisposable d = new FromIterableDisposable<>(observer, it); observer.onSubscribe(d); if (!d.fusionMode) { @@ -88,7 +88,7 @@ void run() { T v; try { - v = ObjectHelper.requireNonNull(it.next(), "The iterator returned a null value"); + v = Objects.requireNonNull(it.next(), "The iterator returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); @@ -138,7 +138,7 @@ public T poll() { checkNext = true; } - return ObjectHelper.requireNonNull(it.next(), "The iterator returned a null value"); + return Objects.requireNonNull(it.next(), "The iterator returned a null value"); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromPublisher.java index 14efd74eba..ade1eeb870 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import org.reactivestreams.*; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromRunnable.java new file mode 100644 index 0000000000..eeda8e4832 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromRunnable.java @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Supplier; +import io.reactivex.rxjava3.internal.fuseable.CancellableQueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Executes an {@link Runnable} and signals its exception or completes normally. + * + * @param the value type + * @since 3.0.0 + */ +public final class ObservableFromRunnable extends Observable implements Supplier { + + final Runnable run; + + public ObservableFromRunnable(Runnable run) { + this.run = run; + } + + @Override + protected void subscribeActual(Observer observer) { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + observer.onSubscribe(qs); + + if (!qs.isDisposed()) { + + try { + run.run(); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + if (!qs.isDisposed()) { + observer.onError(ex); + } else { + RxJavaPlugins.onError(ex); + } + return; + } + + if (!qs.isDisposed()) { + observer.onComplete(); + } + } + } + + @Override + public T get() throws Throwable { + run.run(); + return null; // considered as onComplete() + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplier.java index 44b6dbdd00..7d3cd41480 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public ObservableFromSupplier(Supplier supplier) { @Override public void subscribeActual(Observer observer) { - DeferredScalarDisposable d = new DeferredScalarDisposable(observer); + DeferredScalarDisposable d = new DeferredScalarDisposable<>(observer); observer.onSubscribe(d); if (d.isDisposed()) { return; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromUnsafeSource.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromUnsafeSource.java index d34423bcd4..1f7c7bcccd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromUnsafeSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromUnsafeSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerate.java index df94a37dbd..fee8dfe26e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ public void subscribeActual(Observer observer) { return; } - GeneratorDisposable gd = new GeneratorDisposable(observer, generator, disposeState, state); + GeneratorDisposable gd = new GeneratorDisposable<>(observer, generator, disposeState, state); observer.onSubscribe(gd); gd.run(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupBy.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupBy.java index 821d2e81e1..e1af50378e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupBy.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupBy.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,9 +23,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.observables.GroupedObservable; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableGroupBy extends AbstractObservableWithUpstream> { final Function keySelector; @@ -45,7 +44,7 @@ public ObservableGroupBy(ObservableSource source, @Override public void subscribeActual(Observer> t) { - source.subscribe(new GroupByObserver(t, keySelector, valueSelector, bufferSize, delayError)); + source.subscribe(new GroupByObserver<>(t, keySelector, valueSelector, bufferSize, delayError)); } public static final class GroupByObserver extends AtomicInteger implements Observer, Disposable { @@ -71,7 +70,7 @@ public GroupByObserver(Observer> actual, Functio this.valueSelector = valueSelector; this.bufferSize = bufferSize; this.delayError = delayError; - this.groups = new ConcurrentHashMap>(); + this.groups = new ConcurrentHashMap<>(); this.lazySet(1); } @@ -115,7 +114,7 @@ public void onNext(T t) { V v; try { - v = ObjectHelper.requireNonNull(valueSelector.apply(t), "The value supplied is null"); + v = Objects.requireNonNull(valueSelector.apply(t), "The value supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.dispose(); @@ -140,7 +139,7 @@ public void onNext(T t) { @Override public void onError(Throwable t) { - List> list = new ArrayList>(groups.values()); + List> list = new ArrayList<>(groups.values()); groups.clear(); for (GroupedUnicast e : list) { @@ -152,7 +151,7 @@ public void onError(Throwable t) { @Override public void onComplete() { - List> list = new ArrayList>(groups.values()); + List> list = new ArrayList<>(groups.values()); groups.clear(); for (GroupedUnicast e : list) { @@ -192,8 +191,8 @@ static final class GroupedUnicast extends GroupedObservable { final State state; public static GroupedUnicast createWith(K key, int bufferSize, GroupByObserver parent, boolean delayError) { - State state = new State(bufferSize, parent, key, delayError); - return new GroupedUnicast(key, state); + State state = new State<>(bufferSize, parent, key, delayError); + return new GroupedUnicast<>(key, state); } protected GroupedUnicast(K key, State state) { @@ -233,7 +232,7 @@ static final class State extends AtomicInteger implements Disposable, Obse final AtomicBoolean cancelled = new AtomicBoolean(); - final AtomicReference> actual = new AtomicReference>(); + final AtomicReference> actual = new AtomicReference<>(); final AtomicInteger once = new AtomicInteger(); @@ -243,7 +242,7 @@ static final class State extends AtomicInteger implements Disposable, Obse static final int ABANDONED_HAS_SUBSCRIBER = ABANDONED | HAS_SUBSCRIBER; State(int bufferSize, GroupByObserver parent, K key, boolean delayError) { - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.parent = parent; this.key = key; this.delayError = delayError; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java index b6c9f811f4..4df7fc7bd8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoin.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.observable; @@ -26,9 +23,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.UnicastSubject; @@ -59,7 +55,7 @@ public ObservableGroupJoin( protected void subscribeActual(Observer observer) { GroupJoinDisposable parent = - new GroupJoinDisposable(observer, leftEnd, rightEnd, resultSelector); + new GroupJoinDisposable<>(observer, leftEnd, rightEnd, resultSelector); observer.onSubscribe(parent); @@ -131,10 +127,10 @@ static final class GroupJoinDisposable BiFunction, ? extends R> resultSelector) { this.downstream = actual; this.disposables = new CompositeDisposable(); - this.queue = new SpscLinkedArrayQueue(bufferSize()); - this.lefts = new LinkedHashMap>(); - this.rights = new LinkedHashMap(); - this.error = new AtomicReference(); + this.queue = new SpscLinkedArrayQueue<>(bufferSize()); + this.lefts = new LinkedHashMap<>(); + this.rights = new LinkedHashMap<>(); + this.error = new AtomicReference<>(); this.leftEnd = leftEnd; this.rightEnd = rightEnd; this.resultSelector = resultSelector; @@ -243,7 +239,7 @@ void drain() { ObservableSource p; try { - p = ObjectHelper.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null ObservableSource"); + p = Objects.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null ObservableSource"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -265,7 +261,7 @@ void drain() { R w; try { - w = ObjectHelper.requireNonNull(resultSelector.apply(left, up), "The resultSelector returned a null value"); + w = Objects.requireNonNull(resultSelector.apply(left, up), "The resultSelector returned a null value"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -288,7 +284,7 @@ else if (mode == RIGHT_VALUE) { ObservableSource p; try { - p = ObjectHelper.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null ObservableSource"); + p = Objects.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null ObservableSource"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -320,7 +316,7 @@ else if (mode == LEFT_CLOSE) { up.onComplete(); } } - else if (mode == RIGHT_CLOSE) { + else { LeftRightEndObserver end = (LeftRightEndObserver)val; rights.remove(end.index); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHide.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHide.java index b929da4f78..8d6c7a7b60 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHide.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHide.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public ObservableHide(ObservableSource source) { @Override protected void subscribeActual(Observer o) { - source.subscribe(new HideDisposable(o)); + source.subscribe(new HideDisposable<>(o)); } static final class HideDisposable implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElements.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElements.java index a633c80dbb..6afab8124d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElements.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElements.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ public ObservableIgnoreElements(ObservableSource source) { @Override public void subscribeActual(final Observer t) { - source.subscribe(new IgnoreObservable(t)); + source.subscribe(new IgnoreObservable<>(t)); } static final class IgnoreObservable implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsCompletable.java index f19ea85008..10e4880b55 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,12 +28,12 @@ public ObservableIgnoreElementsCompletable(ObservableSource source) { @Override public void subscribeActual(final CompletableObserver t) { - source.subscribe(new IgnoreObservable(t)); + source.subscribe(new IgnoreObservable<>(t)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableIgnoreElements(source)); + return RxJavaPlugins.onAssembly(new ObservableIgnoreElements<>(source)); } static final class IgnoreObservable implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelper.java index 1665b99014..89822c3e9b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,10 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.TimeUnit; import io.reactivex.rxjava3.core.*; @@ -43,7 +45,7 @@ public S apply(S t1, Emitter t2) throws Throwable { } public static BiFunction, S> simpleGenerator(Consumer> consumer) { - return new SimpleGenerator(consumer); + return new SimpleGenerator<>(consumer); } static final class SimpleBiGenerator implements BiFunction, S> { @@ -61,7 +63,7 @@ public S apply(S t1, Emitter t2) throws Throwable { } public static BiFunction, S> simpleBiGenerator(BiConsumer> consumer) { - return new SimpleBiGenerator(consumer); + return new SimpleBiGenerator<>(consumer); } static final class ItemDelayFunction implements Function> { @@ -73,13 +75,13 @@ static final class ItemDelayFunction implements Function apply(final T v) throws Throwable { - ObservableSource o = ObjectHelper.requireNonNull(itemDelay.apply(v), "The itemDelay returned a null ObservableSource"); - return new ObservableTake(o, 1).map(Functions.justFunction(v)).defaultIfEmpty(v); + ObservableSource o = Objects.requireNonNull(itemDelay.apply(v), "The itemDelay returned a null ObservableSource"); + return new ObservableTake<>(o, 1).map(Functions.justFunction(v)).defaultIfEmpty(v); } } public static Function> itemDelay(final Function> itemDelay) { - return new ItemDelayFunction(itemDelay); + return new ItemDelayFunction<>(itemDelay); } static final class ObserverOnNext implements Consumer { @@ -90,7 +92,7 @@ static final class ObserverOnNext implements Consumer { } @Override - public void accept(T v) throws Exception { + public void accept(T v) { observer.onNext(v); } } @@ -103,7 +105,7 @@ static final class ObserverOnError implements Consumer { } @Override - public void accept(Throwable v) throws Exception { + public void accept(Throwable v) { observer.onError(v); } } @@ -116,21 +118,21 @@ static final class ObserverOnComplete implements Action { } @Override - public void run() throws Exception { + public void run() { observer.onComplete(); } } public static Consumer observerOnNext(Observer observer) { - return new ObserverOnNext(observer); + return new ObserverOnNext<>(observer); } public static Consumer observerOnError(Observer observer) { - return new ObserverOnError(observer); + return new ObserverOnError<>(observer); } public static Action observerOnComplete(Observer observer) { - return new ObserverOnComplete(observer); + return new ObserverOnComplete<>(observer); } static final class FlatMapWithCombinerInner implements Function { @@ -161,15 +163,15 @@ static final class FlatMapWithCombinerOuter implements Function apply(final T t) throws Throwable { @SuppressWarnings("unchecked") - ObservableSource u = (ObservableSource)ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); - return new ObservableMap(u, new FlatMapWithCombinerInner(combiner, t)); + ObservableSource u = (ObservableSource)Objects.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); + return new ObservableMap<>(u, new FlatMapWithCombinerInner(combiner, t)); } } public static Function> flatMapWithCombiner( final Function> mapper, final BiFunction combiner) { - return new FlatMapWithCombinerOuter(combiner, mapper); + return new FlatMapWithCombinerOuter<>(combiner, mapper); } static final class FlatMapIntoIterable implements Function> { @@ -181,36 +183,36 @@ static final class FlatMapIntoIterable implements Function apply(T t) throws Throwable { - return new ObservableFromIterable(ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null Iterable")); + return new ObservableFromIterable<>(Objects.requireNonNull(mapper.apply(t), "The mapper returned a null Iterable")); } } public static Function> flatMapIntoIterable(final Function> mapper) { - return new FlatMapIntoIterable(mapper); + return new FlatMapIntoIterable<>(mapper); } enum MapToInt implements Function { INSTANCE; @Override - public Object apply(Object t) throws Exception { + public Object apply(Object t) { return 0; } } public static Supplier> replaySupplier(final Observable parent) { - return new ReplaySupplier(parent); + return new ReplaySupplier<>(parent); } public static Supplier> replaySupplier(final Observable parent, final int bufferSize, boolean eagerTruncate) { - return new BufferedReplaySupplier(parent, bufferSize, eagerTruncate); + return new BufferedReplaySupplier<>(parent, bufferSize, eagerTruncate); } public static Supplier> replaySupplier(final Observable parent, final int bufferSize, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - return new BufferedTimedReplaySupplier(parent, bufferSize, time, unit, scheduler, eagerTruncate); + return new BufferedTimedReplaySupplier<>(parent, bufferSize, time, unit, scheduler, eagerTruncate); } public static Supplier> replaySupplier(final Observable parent, final long time, final TimeUnit unit, final Scheduler scheduler, boolean eagerTruncate) { - return new TimedReplayCallable(parent, time, unit, scheduler, eagerTruncate); + return new TimedReplayCallable<>(parent, time, unit, scheduler, eagerTruncate); } static final class ReplaySupplier implements Supplier> { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInterval.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInterval.java index 93fee0973d..c61645bdd9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInterval.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInterval.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java index 96d113663e..68d303b6ab 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRange.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -90,8 +90,10 @@ public void run() { downstream.onNext(c); if (c == end) { + if (!isDisposed()) { + downstream.onComplete(); + } DisposableHelper.dispose(this); - downstream.onComplete(); return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoin.java index cf98732637..4a0f95695e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoin.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.observable; @@ -24,10 +21,9 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.observable.ObservableGroupJoin.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableJoin extends AbstractObservableWithUpstream { @@ -57,7 +53,7 @@ public ObservableJoin( protected void subscribeActual(Observer observer) { JoinDisposable parent = - new JoinDisposable( + new JoinDisposable<>( observer, leftEnd, rightEnd, resultSelector); observer.onSubscribe(parent); @@ -116,10 +112,10 @@ static final class JoinDisposable BiFunction resultSelector) { this.downstream = actual; this.disposables = new CompositeDisposable(); - this.queue = new SpscLinkedArrayQueue(bufferSize()); - this.lefts = new LinkedHashMap(); - this.rights = new LinkedHashMap(); - this.error = new AtomicReference(); + this.queue = new SpscLinkedArrayQueue<>(bufferSize()); + this.lefts = new LinkedHashMap<>(); + this.rights = new LinkedHashMap<>(); + this.error = new AtomicReference<>(); this.leftEnd = leftEnd; this.rightEnd = rightEnd; this.resultSelector = resultSelector; @@ -219,7 +215,7 @@ void drain() { ObservableSource p; try { - p = ObjectHelper.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null ObservableSource"); + p = Objects.requireNonNull(leftEnd.apply(left), "The leftEnd returned a null ObservableSource"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -243,7 +239,7 @@ void drain() { R w; try { - w = ObjectHelper.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); + w = Objects.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -263,7 +259,7 @@ else if (mode == RIGHT_VALUE) { ObservableSource p; try { - p = ObjectHelper.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null ObservableSource"); + p = Objects.requireNonNull(rightEnd.apply(right), "The rightEnd returned a null ObservableSource"); } catch (Throwable exc) { fail(exc, a, q); return; @@ -287,7 +283,7 @@ else if (mode == RIGHT_VALUE) { R w; try { - w = ObjectHelper.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); + w = Objects.requireNonNull(resultSelector.apply(left, right), "The resultSelector returned a null value"); } catch (Throwable exc) { fail(exc, a, q); return; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJust.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJust.java index 0fe64a5291..af01eb09bb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJust.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJust.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,8 +14,8 @@ package io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; import io.reactivex.rxjava3.internal.operators.observable.ObservableScalarXMap.ScalarDisposable; +import io.reactivex.rxjava3.operators.ScalarSupplier; /** * Represents a constant scalar value. @@ -30,7 +30,7 @@ public ObservableJust(final T value) { @Override protected void subscribeActual(Observer observer) { - ScalarDisposable sd = new ScalarDisposable(observer, value); + ScalarDisposable sd = new ScalarDisposable<>(observer, value); observer.onSubscribe(sd); sd.run(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastMaybe.java index 79457e0e80..b20119d430 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public ObservableLastMaybe(ObservableSource source) { @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new LastObserver(observer)); + source.subscribe(new LastObserver<>(observer)); } static final class LastObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastSingle.java index 2ba8f28856..049e9341c1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public ObservableLastSingle(ObservableSource source, T defaultItem) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new LastObserver(observer, defaultItem)); + source.subscribe(new LastObserver<>(observer, defaultItem)); } static final class LastObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLift.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLift.java index f38ac66bd3..67dd7a0649 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLift.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLift.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,9 +15,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Allows lifting operators into a chain of Observables. * @@ -40,7 +41,7 @@ public ObservableLift(ObservableSource source, ObservableOperator observer) { Observer liftedObserver; try { - liftedObserver = ObjectHelper.requireNonNull(operator.apply(observer), "Operator " + operator + " returned a null Observer"); + liftedObserver = Objects.requireNonNull(operator.apply(observer), "Operator " + operator + " returned a null Observer"); } catch (NullPointerException e) { // NOPMD throw e; } catch (Throwable e) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMap.java index 9bff5de703..8f4502ba19 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,9 +16,10 @@ import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicFuseableObserver; +import java.util.Objects; + public final class ObservableMap extends AbstractObservableWithUpstream { final Function function; @@ -54,7 +55,7 @@ public void onNext(T t) { U v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); + v = Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value."); } catch (Throwable ex) { fail(ex); return; @@ -71,7 +72,7 @@ public int requestFusion(int mode) { @Override public U poll() throws Throwable { T t = qd.poll(); - return t != null ? ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; + return t != null ? Objects.requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotification.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotification.java index 8ef5a5395d..51883e7519 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotification.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotification.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,8 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class ObservableMapNotification extends AbstractObservableWithUpstream> { @@ -39,7 +40,7 @@ public ObservableMapNotification( @Override public void subscribeActual(Observer> t) { - source.subscribe(new MapNotificationObserver(t, onNextMapper, onErrorMapper, onCompleteSupplier)); + source.subscribe(new MapNotificationObserver<>(t, onNextMapper, onErrorMapper, onCompleteSupplier)); } static final class MapNotificationObserver @@ -84,7 +85,7 @@ public void onNext(T t) { ObservableSource p; try { - p = ObjectHelper.requireNonNull(onNextMapper.apply(t), "The onNext ObservableSource returned is null"); + p = Objects.requireNonNull(onNextMapper.apply(t), "The onNext ObservableSource returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); @@ -99,7 +100,7 @@ public void onError(Throwable t) { ObservableSource p; try { - p = ObjectHelper.requireNonNull(onErrorMapper.apply(t), "The onError ObservableSource returned is null"); + p = Objects.requireNonNull(onErrorMapper.apply(t), "The onError ObservableSource returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(new CompositeException(t, e)); @@ -115,7 +116,7 @@ public void onComplete() { ObservableSource p; try { - p = ObjectHelper.requireNonNull(onCompleteSupplier.get(), "The onComplete ObservableSource returned is null"); + p = Objects.requireNonNull(onCompleteSupplier.get(), "The onComplete ObservableSource returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterialize.java index 6041961bcb..866ced7dd9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public ObservableMaterialize(ObservableSource source) { @Override public void subscribeActual(Observer> t) { - source.subscribe(new MaterializeObserver(t)); + source.subscribe(new MaterializeObserver<>(t)); } static final class MaterializeObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletable.java index 6d022ebf62..e1a79180d8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public ObservableMergeWithCompletable(Observable source, CompletableSource ot @Override protected void subscribeActual(Observer observer) { - MergeWithObserver parent = new MergeWithObserver(observer); + MergeWithObserver parent = new MergeWithObserver<>(observer); observer.onSubscribe(parent); source.subscribe(parent); other.subscribe(parent.otherObserver); @@ -63,7 +63,7 @@ static final class MergeWithObserver extends AtomicInteger MergeWithObserver(Observer downstream) { this.downstream = downstream; - this.mainDisposable = new AtomicReference(); + this.mainDisposable = new AtomicReference<>(); this.otherObserver = new OtherObserver(this); this.errors = new AtomicThrowable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybe.java index d852bff7dd..4e940a74f5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; /** * Merges an Observable and a Maybe by emitting the items of the Observable and the success @@ -40,7 +40,7 @@ public ObservableMergeWithMaybe(Observable source, MaybeSource o @Override protected void subscribeActual(Observer observer) { - MergeWithObserver parent = new MergeWithObserver(observer); + MergeWithObserver parent = new MergeWithObserver<>(observer); observer.onSubscribe(parent); source.subscribe(parent); other.subscribe(parent.otherObserver); @@ -75,8 +75,8 @@ static final class MergeWithObserver extends AtomicInteger MergeWithObserver(Observer downstream) { this.downstream = downstream; - this.mainDisposable = new AtomicReference(); - this.otherObserver = new OtherObserver(this); + this.mainDisposable = new AtomicReference<>(); + this.otherObserver = new OtherObserver<>(this); this.errors = new AtomicThrowable(); } @@ -162,7 +162,7 @@ void otherComplete() { SimplePlainQueue getOrCreateQueue() { SimplePlainQueue q = queue; if (q == null) { - q = new SpscLinkedArrayQueue(bufferSize()); + q = new SpscLinkedArrayQueue<>(bufferSize()); queue = q; } return q; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingle.java index 30e80cf4de..014df98cc2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; /** * Merges an Observable and a Single by emitting the items of the Observable and the success @@ -40,7 +40,7 @@ public ObservableMergeWithSingle(Observable source, SingleSource @Override protected void subscribeActual(Observer observer) { - MergeWithObserver parent = new MergeWithObserver(observer); + MergeWithObserver parent = new MergeWithObserver<>(observer); observer.onSubscribe(parent); source.subscribe(parent); other.subscribe(parent.otherObserver); @@ -75,8 +75,8 @@ static final class MergeWithObserver extends AtomicInteger MergeWithObserver(Observer downstream) { this.downstream = downstream; - this.mainDisposable = new AtomicReference(); - this.otherObserver = new OtherObserver(this); + this.mainDisposable = new AtomicReference<>(); + this.otherObserver = new OtherObserver<>(this); this.errors = new AtomicThrowable(); } @@ -157,7 +157,7 @@ void otherError(Throwable ex) { SimplePlainQueue getOrCreateQueue() { SimplePlainQueue q = queue; if (q == null) { - q = new SpscLinkedArrayQueue(bufferSize()); + q = new SpscLinkedArrayQueue<>(bufferSize()); queue = q; } return q; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableNever.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableNever.java index dd476d5993..1d237491cc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableNever.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableNever.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn.java index 9bc2b39117..05a0064f41 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,11 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.observers.BasicIntQueueDisposable; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.schedulers.TrampolineScheduler; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableObserveOn extends AbstractObservableWithUpstream { @@ -42,7 +43,7 @@ protected void subscribeActual(Observer observer) { } else { Scheduler.Worker w = scheduler.createWorker(); - source.subscribe(new ObserveOnObserver(observer, w, delayError, bufferSize)); + source.subscribe(new ObserveOnObserver<>(observer, w, delayError, bufferSize)); } } @@ -101,7 +102,7 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); downstream.onSubscribe(this); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorComplete.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorComplete.java new file mode 100644 index 0000000000..70266c14d3 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorComplete.java @@ -0,0 +1,106 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.Predicate; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Emits an onComplete if the source emits an onError and the predicate returns true for + * that Throwable. + * + * @param the value type + * @since 3.0.0 + */ +public final class ObservableOnErrorComplete extends AbstractObservableWithUpstream { + + final Predicate predicate; + + public ObservableOnErrorComplete(ObservableSource source, + Predicate predicate) { + super(source); + this.predicate = predicate; + } + + @Override + protected void subscribeActual(Observer observer) { + source.subscribe(new OnErrorCompleteObserver<>(observer, predicate)); + } + + public static final class OnErrorCompleteObserver + implements Observer, Disposable { + + final Observer downstream; + + final Predicate predicate; + + Disposable upstream; + + public OnErrorCompleteObserver(Observer actual, Predicate predicate) { + this.downstream = actual; + this.predicate = predicate; + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onNext(T value) { + downstream.onNext(value); + } + + @Override + public void onError(Throwable e) { + boolean b; + + try { + b = predicate.test(e); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(new CompositeException(e, ex)); + return; + } + + if (b) { + downstream.onComplete(); + } else { + downstream.onError(e); + } + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public void dispose() { + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorNext.java index 093e8d1299..9bcc3b8dd6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public ObservableOnErrorNext(ObservableSource source, @Override public void subscribeActual(Observer t) { - OnErrorNextObserver parent = new OnErrorNextObserver(t, nextSupplier); + OnErrorNextObserver parent = new OnErrorNextObserver<>(t, nextSupplier); t.onSubscribe(parent.arbiter); source.subscribe(parent); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturn.java index df868ab410..24c9af4700 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public ObservableOnErrorReturn(ObservableSource source, Function t) { - source.subscribe(new OnErrorReturnObserver(t, valueSupplier)); + source.subscribe(new OnErrorReturnObserver<>(t, valueSupplier)); } static final class OnErrorReturnObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublish.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublish.java index 629945369e..f2d4c8f0f4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublish.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublish.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -46,7 +46,7 @@ public final class ObservablePublish extends ConnectableObservable public ObservablePublish(ObservableSource source) { this.source = source; - this.current = new AtomicReference>(); + this.current = new AtomicReference<>(); } @Override @@ -58,7 +58,7 @@ public void connect(Consumer connection) { conn = current.get(); if (conn == null || conn.isDisposed()) { - PublishConnection fresh = new PublishConnection(current); + PublishConnection fresh = new PublishConnection<>(current); if (!current.compareAndSet(conn, fresh)) { continue; } @@ -89,7 +89,7 @@ protected void subscribeActual(Observer observer) { conn = current.get(); // we don't create a fresh connection if the current is terminated if (conn == null) { - PublishConnection fresh = new PublishConnection(current); + PublishConnection fresh = new PublishConnection<>(current); if (!current.compareAndSet(conn, fresh)) { continue; } @@ -98,7 +98,7 @@ protected void subscribeActual(Observer observer) { break; } - InnerDisposable inner = new InnerDisposable(observer, conn); + InnerDisposable inner = new InnerDisposable<>(observer, conn); observer.onSubscribe(inner); if (conn.add(inner)) { if (inner.isDisposed()) { @@ -152,7 +152,7 @@ static final class PublishConnection PublishConnection(AtomicReference> current) { this.connect = new AtomicBoolean(); this.current = current; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); lazySet(EMPTY); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishSelector.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishSelector.java index ef7d2367fc..d025a5487d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishSelector.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishSelector.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.subjects.PublishSubject; /** @@ -45,21 +45,21 @@ protected void subscribeActual(Observer observer) { ObservableSource target; try { - target = ObjectHelper.requireNonNull(selector.apply(subject), "The selector returned a null ObservableSource"); + target = Objects.requireNonNull(selector.apply(subject), "The selector returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); return; } - TargetObserver o = new TargetObserver(observer); + TargetObserver o = new TargetObserver<>(observer); target.subscribe(o); - source.subscribe(new SourceObserver(subject, o)); + source.subscribe(new SourceObserver<>(subject, o)); } - static final class SourceObserver implements Observer { + static final class SourceObserver implements Observer { final PublishSubject subject; @@ -91,7 +91,7 @@ public void onComplete() { } } - static final class TargetObserver + static final class TargetObserver extends AtomicReference implements Observer, Disposable { private static final long serialVersionUID = 854110278590336484L; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRange.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRange.java index 2b845fec51..53855a46c2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRange.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRange.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.annotations.Nullable; @@ -71,7 +72,7 @@ void run() { @Nullable @Override - public Integer poll() throws Exception { + public Integer poll() { long i = index; if (i != end) { index = i + 1; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLong.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLong.java index 2a4122ccad..39a36e72f2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLong.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLong.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.annotations.Nullable; @@ -68,7 +69,7 @@ void run() { @Nullable @Override - public Long poll() throws Exception { + public Long poll() { long i = index; if (i != end) { index = i + 1; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceMaybe.java index 24d5e5d568..3f66614ef0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduce a sequence of values into a single value via an aggregator function and emit the final value or complete * if the source is empty. @@ -40,7 +41,7 @@ public ObservableReduceMaybe(ObservableSource source, BiFunction red @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new ReduceObserver(observer, reducer)); + source.subscribe(new ReduceObserver<>(observer, reducer)); } static final class ReduceObserver implements Observer, Disposable { @@ -78,7 +79,7 @@ public void onNext(T value) { this.value = value; } else { try { - this.value = ObjectHelper.requireNonNull(reducer.apply(v, value), "The reducer returned a null value"); + this.value = Objects.requireNonNull(reducer.apply(v, value), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceSeedSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceSeedSingle.java index 4ec8ac8bcc..f2382f6f9d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceSeedSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceSeedSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduce a sequence of values, starting from a seed value and by using * an accumulator function and return the last accumulated value. @@ -44,7 +45,7 @@ public ObservableReduceSeedSingle(ObservableSource source, R seed, BiFunction @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new ReduceSeedObserver(observer, reducer, seed)); + source.subscribe(new ReduceSeedObserver<>(observer, reducer, seed)); } static final class ReduceSeedObserver implements Observer, Disposable { @@ -77,7 +78,7 @@ public void onNext(T value) { R v = this.value; if (v != null) { try { - this.value = ObjectHelper.requireNonNull(reducer.apply(v, value), "The reducer returned a null value"); + this.value = Objects.requireNonNull(reducer.apply(v, value), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceWithSingle.java index e7b1b16813..22dbaa6a1d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,9 +17,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.observable.ObservableReduceSeedSingle.ReduceSeedObserver; +import java.util.Objects; + /** * Reduce a sequence of values, starting from a generated seed value and by using * an accumulator function and return the last accumulated value. @@ -46,12 +47,12 @@ protected void subscribeActual(SingleObserver observer) { R seed; try { - seed = ObjectHelper.requireNonNull(seedSupplier.get(), "The seedSupplier returned a null value"); + seed = Objects.requireNonNull(seedSupplier.get(), "The seedSupplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); return; } - source.subscribe(new ReduceSeedObserver(observer, reducer, seed)); + source.subscribe(new ReduceSeedObserver<>(observer, reducer, seed)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCount.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCount.java index f0b9157204..1a5d9f55cc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCount.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCount.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -81,7 +81,7 @@ protected void subscribeActual(Observer observer) { } } - source.subscribe(new RefCountObserver(observer, this, conn)); + source.subscribe(new RefCountObserver<>(observer, this, conn)); if (connect) { source.connect(conn); @@ -166,7 +166,7 @@ public void run() { } @Override - public void accept(Disposable t) throws Exception { + public void accept(Disposable t) { DisposableHelper.replace(this, t); synchronized (parent) { if (disconnectedEarly) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeat.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeat.java index ad9159137c..b14549f65f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeat.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeat.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public void subscribeActual(Observer observer) { SequentialDisposable sd = new SequentialDisposable(); observer.onSubscribe(sd); - RepeatObserver rs = new RepeatObserver(observer, count != Long.MAX_VALUE ? count - 1 : Long.MAX_VALUE, sd, source); + RepeatObserver rs = new RepeatObserver<>(observer, count != Long.MAX_VALUE ? count - 1 : Long.MAX_VALUE, sd, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatUntil.java index 60637fe802..d89fd17b83 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public void subscribeActual(Observer observer) { SequentialDisposable sd = new SequentialDisposable(); observer.onSubscribe(sd); - RepeatUntilObserver rs = new RepeatUntilObserver(observer, until, sd, source); + RepeatUntilObserver rs = new RepeatUntilObserver<>(observer, until, sd, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatWhen.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatWhen.java index abdf42b872..87d30330ba 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatWhen.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.subjects.*; @@ -45,14 +45,14 @@ protected void subscribeActual(Observer observer) { ObservableSource other; try { - other = ObjectHelper.requireNonNull(handler.apply(signaller), "The handler returned a null ObservableSource"); + other = Objects.requireNonNull(handler.apply(signaller), "The handler returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); return; } - RepeatWhenObserver parent = new RepeatWhenObserver(observer, signaller, source); + RepeatWhenObserver parent = new RepeatWhenObserver<>(observer, signaller, source); observer.onSubscribe(parent); other.subscribe(parent.inner); @@ -87,7 +87,7 @@ static final class RepeatWhenObserver extends AtomicInteger implements Observ this.wip = new AtomicInteger(); this.error = new AtomicThrowable(); this.inner = new InnerRepeatObserver(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java index 1bb4f344d4..0e8c122d62 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.fuseable.HasUpstreamObservableSource; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.observables.ConnectableObservable; @@ -60,7 +59,7 @@ interface BufferSupplier { public static Observable multicastSelector( final Supplier> connectableFactory, final Function, ? extends ObservableSource> selector) { - return RxJavaPlugins.onAssembly(new MulticastReplay(connectableFactory, selector)); + return RxJavaPlugins.onAssembly(new MulticastReplay<>(connectableFactory, selector)); } /** @@ -87,7 +86,7 @@ public static ConnectableObservable create(ObservableSource source, if (bufferSize == Integer.MAX_VALUE) { return createFrom(source); } - return create(source, new ReplayBufferSupplier(bufferSize, eagerTruncate)); + return create(source, new ReplayBufferSupplier<>(bufferSize, eagerTruncate)); } /** @@ -118,11 +117,12 @@ public static ConnectableObservable create(ObservableSource source, */ public static ConnectableObservable create(ObservableSource source, final long maxAge, final TimeUnit unit, final Scheduler scheduler, final int bufferSize, boolean eagerTruncate) { - return create(source, new ScheduledReplaySupplier(bufferSize, maxAge, unit, scheduler, eagerTruncate)); + return create(source, new ScheduledReplaySupplier<>(bufferSize, maxAge, unit, scheduler, eagerTruncate)); } /** * Creates a OperatorReplay instance to replay values of the given source observable. + * @param the value type * @param source the source observable * @param bufferFactory the factory to instantiate the appropriate buffer when the observable becomes active * @return the connectable observable @@ -130,9 +130,9 @@ public static ConnectableObservable create(ObservableSource source, static ConnectableObservable create(ObservableSource source, final BufferSupplier bufferFactory) { // the current connection to source needs to be shared between the operator and its onSubscribe call - final AtomicReference> curr = new AtomicReference>(); - ObservableSource onSubscribe = new ReplaySource(curr, bufferFactory); - return RxJavaPlugins.onAssembly(new ObservableReplay(onSubscribe, source, curr, bufferFactory)); + final AtomicReference> curr = new AtomicReference<>(); + ObservableSource onSubscribe = new ReplaySource<>(curr, bufferFactory); + return RxJavaPlugins.onAssembly(new ObservableReplay<>(onSubscribe, source, curr, bufferFactory)); } private ObservableReplay(ObservableSource onSubscribe, ObservableSource source, @@ -175,7 +175,7 @@ public void connect(Consumer connection) { // create a new subscriber-to-source ReplayBuffer buf = bufferFactory.call(); - ReplayObserver u = new ReplayObserver(buf); + ReplayObserver u = new ReplayObserver<>(buf, current); // try setting it as the current subscriber-to-source if (!current.compareAndSet(ps, u)) { // did not work, perhaps a new subscriber arrived @@ -206,6 +206,7 @@ public void connect(Consumer connection) { try { connection.accept(ps); } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); if (doConnect) { ps.shouldConnect.compareAndSet(true, false); } @@ -240,10 +241,14 @@ static final class ReplayObserver */ final AtomicBoolean shouldConnect; - ReplayObserver(ReplayBuffer buffer) { + /** The current connection. */ + final AtomicReference> current; + + ReplayObserver(ReplayBuffer buffer, AtomicReference> current) { this.buffer = buffer; + this.current = current; - this.observers = new AtomicReference(EMPTY); + this.observers = new AtomicReference<>(EMPTY); this.shouldConnect = new AtomicBoolean(); } @@ -255,9 +260,7 @@ public boolean isDisposed() { @Override public void dispose() { observers.set(TERMINATED); - // unlike OperatorPublish, we can't null out the terminated so - // late observers can still get replay - // current.compareAndSet(ReplayObserver.this, null); + current.compareAndSet(ReplayObserver.this, null); // we don't care if it fails because it means the current has // been replaced in the meantime DisposableHelper.dispose(this); @@ -451,6 +454,7 @@ public void dispose() { } /** * Convenience method to auto-cast the index object. + * @param type index to be casted to * @return the index Object or null */ @SuppressWarnings("unchecked") @@ -829,7 +833,7 @@ static final class SizeAndTimeBoundReplayBuffer extends BoundedReplayBuffer(value, scheduler.now(unit), unit); + return new Timed<>(value, scheduler.now(unit), unit); } @Override @@ -846,7 +850,7 @@ void truncate() { int e = 0; for (;;) { - if (next != null && size > 1) { // never truncate the very last item just added + if (size > 1) { // never truncate the very last item just added if (size > limit) { e++; size--; @@ -881,7 +885,7 @@ void truncateFinal() { int e = 0; for (;;) { - if (next != null && size > 1) { + if (size > 1) { Timed v = (Timed)next.value; if (v.time() <= timeLimit) { e++; @@ -927,7 +931,7 @@ Node getHead() { static final class UnBoundedFactory implements BufferSupplier { @Override public ReplayBuffer call() { - return new UnboundedReplayBuffer(16); + return new UnboundedReplayBuffer<>(16); } } @@ -957,7 +961,7 @@ static final class ReplayBufferSupplier implements BufferSupplier { @Override public ReplayBuffer call() { - return new SizeBoundReplayBuffer(bufferSize, eagerTruncate); + return new SizeBoundReplayBuffer<>(bufferSize, eagerTruncate); } } @@ -979,7 +983,7 @@ static final class ScheduledReplaySupplier implements BufferSupplier { @Override public ReplayBuffer call() { - return new SizeAndTimeBoundReplayBuffer(bufferSize, maxAge, unit, scheduler, eagerTruncate); + return new SizeAndTimeBoundReplayBuffer<>(bufferSize, maxAge, unit, scheduler, eagerTruncate); } } @@ -1004,7 +1008,7 @@ public void subscribe(Observer child) { // create a new subscriber to source ReplayBuffer buf = bufferFactory.call(); - ReplayObserver u = new ReplayObserver(buf); + ReplayObserver u = new ReplayObserver<>(buf, curr); // let's try setting it as the current subscriber-to-source if (!curr.compareAndSet(null, u)) { // didn't work, maybe someone else did it or the current subscriber @@ -1016,7 +1020,7 @@ public void subscribe(Observer child) { } // create the backpressure-managing producer for this child - InnerDisposable inner = new InnerDisposable(r, child); + InnerDisposable inner = new InnerDisposable<>(r, child); // the producer has been registered with the current subscriber-to-source so // at least it will receive the next terminal event // setting the producer will trigger the first request to be considered by @@ -1054,44 +1058,19 @@ protected void subscribeActual(Observer child) { ConnectableObservable co; ObservableSource observable; try { - co = ObjectHelper.requireNonNull(connectableFactory.get(), "The connectableFactory returned a null ConnectableObservable"); - observable = ObjectHelper.requireNonNull(selector.apply(co), "The selector returned a null ObservableSource"); + co = Objects.requireNonNull(connectableFactory.get(), "The connectableFactory returned a null ConnectableObservable"); + observable = Objects.requireNonNull(selector.apply(co), "The selector returned a null ObservableSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptyDisposable.error(e, child); return; } - final ObserverResourceWrapper srw = new ObserverResourceWrapper(child); + final ObserverResourceWrapper srw = new ObserverResourceWrapper<>(child); observable.subscribe(srw); - co.connect(new DisposeConsumer(srw)); - } - } - - static final class Replay extends ConnectableObservable { - private final ConnectableObservable co; - private final Observable observable; - - Replay(ConnectableObservable co, Observable observable) { - this.co = co; - this.observable = observable; - } - - @Override - public void connect(Consumer connection) { - co.connect(connection); - } - - @Override - public void reset() { - co.reset(); - } - - @Override - protected void subscribeActual(Observer observer) { - observable.subscribe(observer); + co.connect(new DisposeConsumer<>(srw)); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryBiPredicate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryBiPredicate.java index 48edbdaa26..1340a5ad9f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryBiPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryBiPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public void subscribeActual(Observer observer) { SequentialDisposable sa = new SequentialDisposable(); observer.onSubscribe(sa); - RetryBiObserver rs = new RetryBiObserver(observer, predicate, sa, source); + RetryBiObserver rs = new RetryBiObserver<>(observer, predicate, sa, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryPredicate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryPredicate.java index 963e5c8890..5c40dc6d97 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public void subscribeActual(Observer observer) { SequentialDisposable sa = new SequentialDisposable(); observer.onSubscribe(sa); - RepeatObserver rs = new RepeatObserver(observer, count, predicate, sa, source); + RepeatObserver rs = new RepeatObserver<>(observer, count, predicate, sa, source); rs.subscribeNext(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWhen.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWhen.java index 054887adab..bae50456e4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWhen.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.subjects.*; @@ -45,14 +45,14 @@ protected void subscribeActual(Observer observer) { ObservableSource other; try { - other = ObjectHelper.requireNonNull(handler.apply(signaller), "The handler returned a null ObservableSource"); + other = Objects.requireNonNull(handler.apply(signaller), "The handler returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); return; } - RepeatWhenObserver parent = new RepeatWhenObserver(observer, signaller, source); + RepeatWhenObserver parent = new RepeatWhenObserver<>(observer, signaller, source); observer.onSubscribe(parent); other.subscribe(parent.inner); @@ -87,7 +87,7 @@ static final class RepeatWhenObserver extends AtomicInteger implements Observ this.wip = new AtomicInteger(); this.error = new AtomicThrowable(); this.inner = new InnerRepeatObserver(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTimed.java index 4a91ff30b8..f264b8e76d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,6 +18,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; import io.reactivex.rxjava3.observers.SerializedObserver; @@ -25,24 +27,30 @@ public final class ObservableSampleTimed extends AbstractObservableWithUpstre final long period; final TimeUnit unit; final Scheduler scheduler; - + final Consumer onDropped; final boolean emitLast; - public ObservableSampleTimed(ObservableSource source, long period, TimeUnit unit, Scheduler scheduler, boolean emitLast) { + public ObservableSampleTimed(ObservableSource source, + long period, + TimeUnit unit, + Scheduler scheduler, + boolean emitLast, + Consumer onDropped) { super(source); this.period = period; this.unit = unit; this.scheduler = scheduler; this.emitLast = emitLast; + this.onDropped = onDropped; } @Override public void subscribeActual(Observer t) { - SerializedObserver serial = new SerializedObserver(t); + SerializedObserver serial = new SerializedObserver<>(t); if (emitLast) { - source.subscribe(new SampleTimedEmitLast(serial, period, unit, scheduler)); + source.subscribe(new SampleTimedEmitLast<>(serial, period, unit, scheduler, onDropped)); } else { - source.subscribe(new SampleTimedNoLast(serial, period, unit, scheduler)); + source.subscribe(new SampleTimedNoLast<>(serial, period, unit, scheduler, onDropped)); } } @@ -54,16 +62,18 @@ abstract static class SampleTimedObserver extends AtomicReference implemen final long period; final TimeUnit unit; final Scheduler scheduler; + final Consumer onDropped; - final AtomicReference timer = new AtomicReference(); + final AtomicReference timer = new AtomicReference<>(); Disposable upstream; - SampleTimedObserver(Observer actual, long period, TimeUnit unit, Scheduler scheduler) { + SampleTimedObserver(Observer actual, long period, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { this.downstream = actual; this.period = period; this.unit = unit; this.scheduler = scheduler; + this.onDropped = onDropped; } @Override @@ -79,7 +89,17 @@ public void onSubscribe(Disposable d) { @Override public void onNext(T t) { - lazySet(t); + T oldValue = getAndSet(t); + if (oldValue != null && onDropped != null) { + try { + onDropped.accept(oldValue); + } catch (Throwable throwable) { + Exceptions.throwIfFatal(throwable); + cancelTimer(); + upstream.dispose(); + downstream.onError(throwable); + } + } } @Override @@ -123,8 +143,8 @@ static final class SampleTimedNoLast extends SampleTimedObserver { private static final long serialVersionUID = -7139995637533111443L; - SampleTimedNoLast(Observer actual, long period, TimeUnit unit, Scheduler scheduler) { - super(actual, period, unit, scheduler); + SampleTimedNoLast(Observer actual, long period, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { + super(actual, period, unit, scheduler, onDropped); } @Override @@ -144,8 +164,8 @@ static final class SampleTimedEmitLast extends SampleTimedObserver { final AtomicInteger wip; - SampleTimedEmitLast(Observer actual, long period, TimeUnit unit, Scheduler scheduler) { - super(actual, period, unit, scheduler); + SampleTimedEmitLast(Observer actual, long period, TimeUnit unit, Scheduler scheduler, Consumer onDropped) { + super(actual, period, unit, scheduler, onDropped); this.wip = new AtomicInteger(1); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleWithObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleWithObservable.java index 4c0fccc654..53b9aec31f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleWithObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleWithObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,11 +34,11 @@ public ObservableSampleWithObservable(ObservableSource source, ObservableSour @Override public void subscribeActual(Observer t) { - SerializedObserver serial = new SerializedObserver(t); + SerializedObserver serial = new SerializedObserver<>(t); if (emitLast) { - source.subscribe(new SampleMainEmitLast(serial, other)); + source.subscribe(new SampleMainEmitLast<>(serial, other)); } else { - source.subscribe(new SampleMainNoLast(serial, other)); + source.subscribe(new SampleMainNoLast<>(serial, other)); } } @@ -50,7 +50,7 @@ abstract static class SampleMainObserver extends AtomicReference final Observer downstream; final ObservableSource sampler; - final AtomicReference other = new AtomicReference(); + final AtomicReference other = new AtomicReference<>(); Disposable upstream; @@ -65,7 +65,7 @@ public void onSubscribe(Disposable d) { this.upstream = d; downstream.onSubscribe(this); if (other.get() == null) { - sampler.subscribe(new SamplerObserver(this)); + sampler.subscribe(new SamplerObserver<>(this)); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMap.java index 85c13e27e7..7ce5bb82f0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.rxjava3.annotations.Nullable; @@ -20,8 +21,7 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueDisposable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -66,7 +66,7 @@ public static boolean tryScalarXMapSubscribe(ObservableSource source, ObservableSource r; try { - r = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); + r = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null ObservableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); @@ -88,7 +88,7 @@ public static boolean tryScalarXMapSubscribe(ObservableSource source, EmptyDisposable.complete(observer); return true; } - ScalarDisposable sd = new ScalarDisposable(observer, u); + ScalarDisposable sd = new ScalarDisposable<>(observer, u); observer.onSubscribe(sd); sd.run(); } else { @@ -112,7 +112,7 @@ public static boolean tryScalarXMapSubscribe(ObservableSource source, */ public static Observable scalarXMap(T value, Function> mapper) { - return RxJavaPlugins.onAssembly(new ScalarXMapObservable(value, mapper)); + return RxJavaPlugins.onAssembly(new ScalarXMapObservable<>(value, mapper)); } /** @@ -138,8 +138,9 @@ static final class ScalarXMapObservable extends Observable { public void subscribeActual(Observer observer) { ObservableSource other; try { - other = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null ObservableSource"); + other = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null ObservableSource"); } catch (Throwable e) { + Exceptions.throwIfFatal(e); EmptyDisposable.error(e, observer); return; } @@ -158,7 +159,7 @@ public void subscribeActual(Observer observer) { EmptyDisposable.complete(observer); return; } - ScalarDisposable sd = new ScalarDisposable(observer, u); + ScalarDisposable sd = new ScalarDisposable<>(observer, u); observer.onSubscribe(sd); sd.run(); } else { @@ -204,7 +205,7 @@ public boolean offer(T v1, T v2) { @Nullable @Override - public T poll() throws Exception { + public T poll() { if (get() == FUSED) { lazySet(ON_COMPLETE); return value; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScan.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScan.java index 026b3ac5eb..59a74661cc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScan.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScan.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class ObservableScan extends AbstractObservableWithUpstream { final BiFunction accumulator; public ObservableScan(ObservableSource source, BiFunction accumulator) { @@ -30,7 +31,7 @@ public ObservableScan(ObservableSource source, BiFunction accumulato @Override public void subscribeActual(Observer t) { - source.subscribe(new ScanObserver(t, accumulator)); + source.subscribe(new ScanObserver<>(t, accumulator)); } static final class ScanObserver implements Observer, Disposable { @@ -80,7 +81,7 @@ public void onNext(T t) { T u; try { - u = ObjectHelper.requireNonNull(accumulator.apply(v, t), "The value returned by the accumulator is null"); + u = Objects.requireNonNull(accumulator.apply(v, t), "The value returned by the accumulator is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanSeed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanSeed.java index e13c6fe189..f0e197db7e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanSeed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanSeed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -17,9 +18,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + public final class ObservableScanSeed extends AbstractObservableWithUpstream { final BiFunction accumulator; final Supplier seedSupplier; @@ -35,14 +37,14 @@ public void subscribeActual(Observer t) { R r; try { - r = ObjectHelper.requireNonNull(seedSupplier.get(), "The seed supplied is null"); + r = Objects.requireNonNull(seedSupplier.get(), "The seed supplied is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptyDisposable.error(e, t); return; } - source.subscribe(new ScanSeedObserver(t, accumulator, r)); + source.subscribe(new ScanSeedObserver<>(t, accumulator, r)); } static final class ScanSeedObserver implements Observer, Disposable { @@ -93,7 +95,7 @@ public void onNext(T t) { R u; try { - u = ObjectHelper.requireNonNull(accumulator.apply(v, t), "The accumulator returned a null value"); + u = Objects.requireNonNull(accumulator.apply(v, t), "The accumulator returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqual.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqual.java index 1e779db18e..e79f992243 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqual.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqual.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,7 +20,7 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiPredicate; import io.reactivex.rxjava3.internal.disposables.ArrayCompositeDisposable; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableSequenceEqual extends Observable { final ObservableSource first; @@ -38,7 +38,7 @@ public ObservableSequenceEqual(ObservableSource first, ObservableSo @Override public void subscribeActual(Observer observer) { - EqualCoordinator ec = new EqualCoordinator(observer, bufferSize, first, second, comparer); + EqualCoordinator ec = new EqualCoordinator<>(observer, bufferSize, first, second, comparer); observer.onSubscribe(ec); ec.subscribe(); } @@ -69,8 +69,8 @@ static final class EqualCoordinator extends AtomicInteger implements Disposab @SuppressWarnings("unchecked") EqualObserver[] as = new EqualObserver[2]; this.observers = as; - as[0] = new EqualObserver(this, 0, bufferSize); - as[1] = new EqualObserver(this, 1, bufferSize); + as[0] = new EqualObserver<>(this, 0, bufferSize); + as[1] = new EqualObserver<>(this, 1, bufferSize); this.resources = new ArrayCompositeDisposable(2); } @@ -226,7 +226,7 @@ static final class EqualObserver implements Observer { EqualObserver(EqualCoordinator parent, int index, int bufferSize) { this.parent = parent; this.index = index; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualSingle.java index 165a8ab64a..9b51815ce0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,7 @@ import io.reactivex.rxjava3.functions.BiPredicate; import io.reactivex.rxjava3.internal.disposables.ArrayCompositeDisposable; import io.reactivex.rxjava3.internal.fuseable.FuseToObservable; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableSequenceEqualSingle extends Single implements FuseToObservable { @@ -40,14 +40,14 @@ public ObservableSequenceEqualSingle(ObservableSource first, Observ @Override public void subscribeActual(SingleObserver observer) { - EqualCoordinator ec = new EqualCoordinator(observer, bufferSize, first, second, comparer); + EqualCoordinator ec = new EqualCoordinator<>(observer, bufferSize, first, second, comparer); observer.onSubscribe(ec); ec.subscribe(); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableSequenceEqual(first, second, comparer, bufferSize)); + return RxJavaPlugins.onAssembly(new ObservableSequenceEqual<>(first, second, comparer, bufferSize)); } static final class EqualCoordinator extends AtomicInteger implements Disposable { @@ -76,8 +76,8 @@ static final class EqualCoordinator extends AtomicInteger implements Disposab @SuppressWarnings("unchecked") EqualObserver[] as = new EqualObserver[2]; this.observers = as; - as[0] = new EqualObserver(this, 0, bufferSize); - as[1] = new EqualObserver(this, 1, bufferSize); + as[0] = new EqualObserver<>(this, 0, bufferSize); + as[1] = new EqualObserver<>(this, 1, bufferSize); this.resources = new ArrayCompositeDisposable(2); } @@ -230,7 +230,7 @@ static final class EqualObserver implements Observer { EqualObserver(EqualCoordinator parent, int index, int bufferSize) { this.parent = parent; this.index = index; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerialized.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerialized.java index 35ce558744..c57d0d9a24 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerialized.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerialized.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -22,6 +23,6 @@ public ObservableSerialized(Observable upstream) { @Override protected void subscribeActual(Observer observer) { - source.subscribe(new SerializedObserver(observer)); + source.subscribe(new SerializedObserver<>(observer)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleMaybe.java index b0bba864b9..b4068e834d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public ObservableSingleMaybe(ObservableSource source) { @Override public void subscribeActual(MaybeObserver t) { - source.subscribe(new SingleElementObserver(t)); + source.subscribe(new SingleElementObserver<>(t)); } static final class SingleElementObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleSingle.java index f7c8f597e1..dd8032a400 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public ObservableSingleSingle(ObservableSource source, T defaultVal @Override public void subscribeActual(SingleObserver t) { - source.subscribe(new SingleElementObserver(t, defaultValue)); + source.subscribe(new SingleElementObserver<>(t, defaultValue)); } static final class SingleElementObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkip.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkip.java index 9812b33663..0fe5787c1b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkip.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkip.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public ObservableSkip(ObservableSource source, long n) { @Override public void subscribeActual(Observer observer) { - source.subscribe(new SkipObserver(observer, n)); + source.subscribe(new SkipObserver<>(observer, n)); } static final class SkipObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLast.java index 7d7ea2383e..68fabb3ac7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLast.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public ObservableSkipLast(ObservableSource source, int skip) { @Override public void subscribeActual(Observer observer) { - source.subscribe(new SkipLastObserver(observer, skip)); + source.subscribe(new SkipLastObserver<>(observer, skip)); } static final class SkipLastObserver extends ArrayDeque implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimed.java index 41b34b6186..7cc6f34f24 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableSkipLastTimed extends AbstractObservableWithUpstream { final long time; @@ -40,7 +40,7 @@ public ObservableSkipLastTimed(ObservableSource source, @Override public void subscribeActual(Observer t) { - source.subscribe(new SkipLastTimedObserver(t, time, unit, scheduler, bufferSize, delayError)); + source.subscribe(new SkipLastTimedObserver<>(t, time, unit, scheduler, bufferSize, delayError)); } static final class SkipLastTimedObserver extends AtomicInteger implements Observer, Disposable { @@ -65,7 +65,7 @@ static final class SkipLastTimedObserver extends AtomicInteger implements Obs this.time = time; this.unit = unit; this.scheduler = scheduler; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.delayError = delayError; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntil.java index 9f0377b784..87535fabf2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,13 +28,13 @@ public ObservableSkipUntil(ObservableSource source, ObservableSource other @Override public void subscribeActual(Observer child) { - final SerializedObserver serial = new SerializedObserver(child); + final SerializedObserver serial = new SerializedObserver<>(child); final ArrayCompositeDisposable frc = new ArrayCompositeDisposable(2); serial.onSubscribe(frc); - final SkipUntilObserver sus = new SkipUntilObserver(serial, frc); + final SkipUntilObserver sus = new SkipUntilObserver<>(serial, frc); other.subscribe(new SkipUntil(frc, sus, serial)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhile.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhile.java index 04326b2647..1614b0b472 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhile.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhile.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public ObservableSkipWhile(ObservableSource source, Predicate pred @Override public void subscribeActual(Observer observer) { - source.subscribe(new SkipWhileObserver(observer, predicate)); + source.subscribe(new SkipWhileObserver<>(observer, predicate)); } static final class SkipWhileObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOn.java index f1fce38571..5eaeaa5035 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public ObservableSubscribeOn(ObservableSource source, Scheduler scheduler) { @Override public void subscribeActual(final Observer observer) { - final SubscribeOnObserver parent = new SubscribeOnObserver(observer); + final SubscribeOnObserver parent = new SubscribeOnObserver<>(observer); observer.onSubscribe(parent); @@ -45,7 +45,7 @@ static final class SubscribeOnObserver extends AtomicReference im SubscribeOnObserver(Observer downstream) { this.downstream = downstream; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmpty.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmpty.java index 4b41661f1d..6aa1d84ecf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmpty.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmpty.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public ObservableSwitchIfEmpty(ObservableSource source, ObservableSource t) { - SwitchIfEmptyObserver parent = new SwitchIfEmptyObserver(t, other); + SwitchIfEmptyObserver parent = new SwitchIfEmptyObserver<>(t, other); t.onSubscribe(parent.arbiter); source.subscribe(parent); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java index d9d50fc032..3e0558aacf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,10 +21,10 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.AtomicThrowable; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableSwitchMap extends AbstractObservableWithUpstream { @@ -48,7 +49,7 @@ public void subscribeActual(Observer t) { return; } - source.subscribe(new SwitchMapObserver(t, mapper, bufferSize, delayErrors)); + source.subscribe(new SwitchMapObserver<>(t, mapper, bufferSize, delayErrors)); } static final class SwitchMapObserver extends AtomicInteger implements Observer, Disposable { @@ -68,11 +69,11 @@ static final class SwitchMapObserver extends AtomicInteger implements Obse Disposable upstream; - final AtomicReference> active = new AtomicReference>(); + final AtomicReference> active = new AtomicReference<>(); static final SwitchMapInnerObserver CANCELLED; static { - CANCELLED = new SwitchMapInnerObserver(null, -1L, 1); + CANCELLED = new SwitchMapInnerObserver<>(null, -1L, 1); CANCELLED.cancel(); } @@ -108,7 +109,7 @@ public void onNext(T t) { ObservableSource p; try { - p = ObjectHelper.requireNonNull(mapper.apply(t), "The ObservableSource returned is null"); + p = Objects.requireNonNull(mapper.apply(t), "The ObservableSource returned is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); upstream.dispose(); @@ -116,7 +117,7 @@ public void onNext(T t) { return; } - SwitchMapInnerObserver nextInner = new SwitchMapInnerObserver(this, c, bufferSize); + SwitchMapInnerObserver nextInner = new SwitchMapInnerObserver<>(this, c, bufferSize); for (;;) { inner = active.get(); @@ -169,12 +170,9 @@ public boolean isDisposed() { @SuppressWarnings("unchecked") void disposeInner() { - SwitchMapInnerObserver a = active.get(); - if (a != CANCELLED) { - a = active.getAndSet((SwitchMapInnerObserver)CANCELLED); - if (a != CANCELLED && a != null) { - a.cancel(); - } + SwitchMapInnerObserver a = active.getAndSet((SwitchMapInnerObserver)CANCELLED); + if (a != null) { + a.cancel(); } } @@ -226,25 +224,6 @@ void drain() { SimpleQueue q = inner.queue; if (q != null) { - if (inner.done) { - boolean empty = q.isEmpty(); - if (delayErrors) { - if (empty) { - active.compareAndSet(inner, null); - continue; - } - } else { - Throwable ex = errors.get(); - if (ex != null) { - errors.tryTerminateConsumer(a); - return; - } - if (empty) { - active.compareAndSet(inner, null); - continue; - } - } - } boolean retry = false; @@ -364,15 +343,16 @@ public void onSubscribe(Disposable d) { } } - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); } } @Override public void onNext(R t) { - if (index == parent.unique) { + SimpleQueue q = queue; + if (index == parent.unique && q != null) { if (t != null) { - queue.offer(t); + q.offer(t); } parent.drain(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTake.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTake.java index 7e675f061c..8cc73b1b56 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTake.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTake.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public ObservableTake(ObservableSource source, long limit) { @Override protected void subscribeActual(Observer observer) { - source.subscribe(new TakeObserver(observer, limit)); + source.subscribe(new TakeObserver<>(observer, limit)); } static final class TakeObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java index 45566a64e7..b0b04c141d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLast.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public ObservableTakeLast(ObservableSource source, int count) { @Override public void subscribeActual(Observer t) { - source.subscribe(new TakeLastObserver(t, count)); + source.subscribe(new TakeLastObserver<>(t, count)); } static final class TakeLastObserver extends ArrayDeque implements Observer, Disposable { @@ -77,9 +77,7 @@ public void onComplete() { } T v = poll(); if (v == null) { - if (!cancelled) { - a.onComplete(); - } + a.onComplete(); return; } a.onNext(v); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOne.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOne.java index 8f176bfebd..cb243dd3bd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOne.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOne.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import io.reactivex.rxjava3.core.*; @@ -24,7 +25,7 @@ public ObservableTakeLastOne(ObservableSource source) { @Override public void subscribeActual(Observer observer) { - source.subscribe(new TakeLastOneObserver(observer)); + source.subscribe(new TakeLastOneObserver<>(observer)); } static final class TakeLastOneObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimed.java index 0b9e16e26f..33066f374b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableTakeLastTimed extends AbstractObservableWithUpstream { final long count; @@ -42,7 +42,7 @@ public ObservableTakeLastTimed(ObservableSource source, @Override public void subscribeActual(Observer t) { - source.subscribe(new TakeLastTimedObserver(t, count, time, unit, scheduler, bufferSize, delayError)); + source.subscribe(new TakeLastTimedObserver<>(t, count, time, unit, scheduler, bufferSize, delayError)); } static final class TakeLastTimedObserver @@ -69,7 +69,7 @@ static final class TakeLastTimedObserver this.time = time; this.unit = unit; this.scheduler = scheduler; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); this.delayError = delayError; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntil.java index fdb376c57e..c32f3a6f65 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public ObservableTakeUntil(ObservableSource source, ObservableSource child) { - TakeUntilMainObserver parent = new TakeUntilMainObserver(child); + TakeUntilMainObserver parent = new TakeUntilMainObserver<>(child); child.onSubscribe(parent); other.subscribe(parent.otherObserver); @@ -53,7 +53,7 @@ static final class TakeUntilMainObserver extends AtomicInteger TakeUntilMainObserver(Observer downstream) { this.downstream = downstream; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.otherObserver = new OtherObserver(); this.error = new AtomicThrowable(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicate.java index d38aabd839..5183a6026e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public ObservableTakeUntilPredicate(ObservableSource source, Predicate observer) { - source.subscribe(new TakeUntilPredicateObserver(observer, predicate)); + source.subscribe(new TakeUntilPredicateObserver<>(observer, predicate)); } static final class TakeUntilPredicateObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhile.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhile.java index 62cb16d791..1b56e6cf34 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhile.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhile.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public ObservableTakeWhile(ObservableSource source, Predicate pred @Override public void subscribeActual(Observer t) { - source.subscribe(new TakeWhileObserver(t, predicate)); + source.subscribe(new TakeWhileObserver<>(t, predicate)); } static final class TakeWhileObserver implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java index 5708f04816..6bf3b9f119 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,28 +19,36 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; import io.reactivex.rxjava3.observers.SerializedObserver; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableThrottleFirstTimed extends AbstractObservableWithUpstream { final long timeout; final TimeUnit unit; final Scheduler scheduler; - - public ObservableThrottleFirstTimed(ObservableSource source, - long timeout, TimeUnit unit, Scheduler scheduler) { + final Consumer onDropped; + + public ObservableThrottleFirstTimed( + ObservableSource source, + long timeout, + TimeUnit unit, + Scheduler scheduler, + Consumer onDropped) { super(source); this.timeout = timeout; this.unit = unit; this.scheduler = scheduler; + this.onDropped = onDropped; } @Override public void subscribeActual(Observer t) { - source.subscribe(new DebounceTimedObserver( - new SerializedObserver(t), - timeout, unit, scheduler.createWorker())); + source.subscribe(new DebounceTimedObserver<>( + new SerializedObserver<>(t), + timeout, unit, scheduler.createWorker(), + onDropped)); } static final class DebounceTimedObserver @@ -52,18 +60,21 @@ static final class DebounceTimedObserver final long timeout; final TimeUnit unit; final Scheduler.Worker worker; - + final Consumer onDropped; Disposable upstream; - volatile boolean gate; - boolean done; - - DebounceTimedObserver(Observer actual, long timeout, TimeUnit unit, Worker worker) { + DebounceTimedObserver( + Observer actual, + long timeout, + TimeUnit unit, + Worker worker, + Consumer onDropped) { this.downstream = actual; this.timeout = timeout; this.unit = unit; this.worker = worker; + this.onDropped = onDropped; } @Override @@ -76,7 +87,7 @@ public void onSubscribe(Disposable d) { @Override public void onNext(T t) { - if (!gate && !done) { + if (!gate) { gate = true; downstream.onNext(t); @@ -86,6 +97,15 @@ public void onNext(T t) { d.dispose(); } DisposableHelper.replace(this, worker.schedule(this, timeout, unit)); + } else if (onDropped != null) { + try { + onDropped.accept(t); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + downstream.onError(ex); + worker.dispose(); + } } } @@ -96,22 +116,14 @@ public void run() { @Override public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - } else { - done = true; - downstream.onError(t); - worker.dispose(); - } + downstream.onError(t); + worker.dispose(); } @Override public void onComplete() { - if (!done) { - done = true; - downstream.onComplete(); - worker.dispose(); - } + downstream.onComplete(); + worker.dispose(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatest.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatest.java index 9d23714549..caf14d3a5e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatest.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * Emits the next or latest item when the given time elapses. @@ -41,19 +44,24 @@ public final class ObservableThrottleLatest extends AbstractObservableWithUps final boolean emitLast; + final Consumer onDropped; + public ObservableThrottleLatest(Observable source, - long timeout, TimeUnit unit, Scheduler scheduler, - boolean emitLast) { + long timeout, TimeUnit unit, + Scheduler scheduler, + boolean emitLast, + Consumer onDropped) { super(source); this.timeout = timeout; this.unit = unit; this.scheduler = scheduler; this.emitLast = emitLast; + this.onDropped = onDropped; } @Override protected void subscribeActual(Observer observer) { - source.subscribe(new ThrottleLatestObserver(observer, timeout, unit, scheduler.createWorker(), emitLast)); + source.subscribe(new ThrottleLatestObserver<>(observer, timeout, unit, scheduler.createWorker(), emitLast, onDropped)); } static final class ThrottleLatestObserver @@ -74,6 +82,8 @@ static final class ThrottleLatestObserver final AtomicReference latest; + final Consumer onDropped; + Disposable upstream; volatile boolean done; @@ -86,14 +96,17 @@ static final class ThrottleLatestObserver boolean timerRunning; ThrottleLatestObserver(Observer downstream, - long timeout, TimeUnit unit, Scheduler.Worker worker, - boolean emitLast) { + long timeout, TimeUnit unit, + Scheduler.Worker worker, + boolean emitLast, + Consumer onDropped) { this.downstream = downstream; this.timeout = timeout; this.unit = unit; this.worker = worker; this.emitLast = emitLast; - this.latest = new AtomicReference(); + this.latest = new AtomicReference<>(); + this.onDropped = onDropped; } @Override @@ -106,7 +119,17 @@ public void onSubscribe(Disposable d) { @Override public void onNext(T t) { - latest.set(t); + T old = latest.getAndSet(t); + if (onDropped != null && old != null) { + try { + onDropped.accept(old); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + error = ex; + done = true; + } + } drain(); } @@ -129,6 +152,22 @@ public void dispose() { upstream.dispose(); worker.dispose(); if (getAndIncrement() == 0) { + clear(); + } + } + + void clear() { + if (onDropped != null) { + T v = latest.getAndSet(null); + if (v != null) { + try { + onDropped.accept(v); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + RxJavaPlugins.onError(ex); + } + } + } else { latest.lazySet(null); } } @@ -158,14 +197,27 @@ void drain() { for (;;) { if (cancelled) { - latest.lazySet(null); + clear(); return; } boolean d = done; + Throwable error = this.error; if (d && error != null) { - latest.lazySet(null); + if (onDropped != null) { + T v = latest.getAndSet(null); + if (v != null) { + try { + onDropped.accept(v); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + error = new CompositeException(error, ex); + } + } + } else { + latest.lazySet(null); + } downstream.onError(error); worker.dispose(); return; @@ -175,9 +227,22 @@ void drain() { boolean empty = v == null; if (d) { - v = latest.getAndSet(null); - if (!empty && emitLast) { - downstream.onNext(v); + if (!empty) { + v = latest.getAndSet(null); + if (emitLast) { + downstream.onNext(v); + } else { + if (onDropped != null) { + try { + onDropped.accept(v); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + worker.dispose(); + return; + } + } + } } downstream.onComplete(); worker.dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeInterval.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeInterval.java index f5ad5a52f5..4a4e6a787f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeInterval.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeInterval.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public ObservableTimeInterval(ObservableSource source, TimeUnit unit, Schedul @Override public void subscribeActual(Observer> t) { - source.subscribe(new TimeIntervalObserver(t, unit, scheduler)); + source.subscribe(new TimeIntervalObserver<>(t, unit, scheduler)); } static final class TimeIntervalObserver implements Observer, Disposable { @@ -75,7 +75,7 @@ public void onNext(T t) { long last = lastTime; lastTime = now; long delta = now - last; - downstream.onNext(new Timed(t, delta, unit)); + downstream.onNext(new Timed<>(t, delta, unit)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeout.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeout.java index 4fe72feadd..1922251d54 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeout.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeout.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.*; @@ -21,7 +22,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.observable.ObservableTimeoutTimed.TimeoutSupport; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -44,12 +44,12 @@ public ObservableTimeout( @Override protected void subscribeActual(Observer observer) { if (other == null) { - TimeoutObserver parent = new TimeoutObserver(observer, itemTimeoutIndicator); + TimeoutObserver parent = new TimeoutObserver<>(observer, itemTimeoutIndicator); observer.onSubscribe(parent); parent.startFirstTimeout(firstTimeoutIndicator); source.subscribe(parent); } else { - TimeoutFallbackObserver parent = new TimeoutFallbackObserver(observer, itemTimeoutIndicator, other); + TimeoutFallbackObserver parent = new TimeoutFallbackObserver<>(observer, itemTimeoutIndicator, other); observer.onSubscribe(parent); parent.startFirstTimeout(firstTimeoutIndicator); source.subscribe(parent); @@ -77,7 +77,7 @@ static final class TimeoutObserver extends AtomicLong this.downstream = actual; this.itemTimeoutIndicator = itemTimeoutIndicator; this.task = new SequentialDisposable(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override @@ -102,7 +102,7 @@ public void onNext(T t) { ObservableSource itemTimeoutObservableSource; try { - itemTimeoutObservableSource = ObjectHelper.requireNonNull( + itemTimeoutObservableSource = Objects.requireNonNull( itemTimeoutIndicator.apply(t), "The itemTimeoutIndicator returned a null ObservableSource."); } catch (Throwable ex) { @@ -206,7 +206,7 @@ static final class TimeoutFallbackObserver this.task = new SequentialDisposable(); this.fallback = fallback; this.index = new AtomicLong(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override @@ -231,7 +231,7 @@ public void onNext(T t) { ObservableSource itemTimeoutObservableSource; try { - itemTimeoutObservableSource = ObjectHelper.requireNonNull( + itemTimeoutObservableSource = Objects.requireNonNull( itemTimeoutIndicator.apply(t), "The itemTimeoutIndicator returned a null ObservableSource."); } catch (Throwable ex) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTimed.java index 0553c9474f..d0f5695143 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,12 +41,12 @@ public ObservableTimeoutTimed(Observable source, @Override protected void subscribeActual(Observer observer) { if (other == null) { - TimeoutObserver parent = new TimeoutObserver(observer, timeout, unit, scheduler.createWorker()); + TimeoutObserver parent = new TimeoutObserver<>(observer, timeout, unit, scheduler.createWorker()); observer.onSubscribe(parent); parent.startTimeout(0L); source.subscribe(parent); } else { - TimeoutFallbackObserver parent = new TimeoutFallbackObserver(observer, timeout, unit, scheduler.createWorker(), other); + TimeoutFallbackObserver parent = new TimeoutFallbackObserver<>(observer, timeout, unit, scheduler.createWorker(), other); observer.onSubscribe(parent); parent.startTimeout(0L); source.subscribe(parent); @@ -76,7 +76,7 @@ static final class TimeoutObserver extends AtomicLong this.unit = unit; this.worker = worker; this.task = new SequentialDisposable(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override @@ -196,7 +196,7 @@ static final class TimeoutFallbackObserver extends AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimer.java index 778388f39b..3c9500996e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java index 9cde3b4033..e75986c77c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToList.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,7 +20,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.util.ExceptionHelper; public final class ObservableToList> @@ -28,12 +27,6 @@ public final class ObservableToList> final Supplier collectionSupplier; - @SuppressWarnings({ "unchecked", "rawtypes" }) - public ObservableToList(ObservableSource source, final int defaultCapacityHint) { - super(source); - this.collectionSupplier = (Supplier)Functions.createArrayList(defaultCapacityHint); - } - public ObservableToList(ObservableSource source, Supplier collectionSupplier) { super(source); this.collectionSupplier = collectionSupplier; @@ -49,7 +42,7 @@ public void subscribeActual(Observer t) { EmptyDisposable.error(e, t); return; } - source.subscribe(new ToListObserver(t, coll)); + source.subscribe(new ToListObserver<>(t, coll)); } static final class ToListObserver> implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListSingle.java index 6258313b61..de4db3ecf1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,12 +53,12 @@ public void subscribeActual(SingleObserver t) { EmptyDisposable.error(e, t); return; } - source.subscribe(new ToListObserver(t, coll)); + source.subscribe(new ToListObserver<>(t, coll)); } @Override public Observable fuseToObservable() { - return RxJavaPlugins.onAssembly(new ObservableToList(source, collectionSupplier)); + return RxJavaPlugins.onAssembly(new ObservableToList<>(source, collectionSupplier)); } static final class ToListObserver> implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOn.java index d012b7cc4d..d06d64f34c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public ObservableUnsubscribeOn(ObservableSource source, Scheduler scheduler) @Override public void subscribeActual(Observer t) { - source.subscribe(new UnsubscribeObserver(t, scheduler)); + source.subscribe(new UnsubscribeObserver<>(t, scheduler)); } static final class UnsubscribeObserver extends AtomicBoolean implements Observer, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java index 3a4e5d81e2..1ffdef32d6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsing.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableUsing extends Observable { @@ -53,7 +53,7 @@ public void subscribeActual(Observer observer) { ObservableSource source; try { - source = ObjectHelper.requireNonNull(sourceSupplier.apply(resource), "The sourceSupplier returned a null ObservableSource"); + source = Objects.requireNonNull(sourceSupplier.apply(resource), "The sourceSupplier returned a null ObservableSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); try { @@ -67,7 +67,7 @@ public void subscribeActual(Observer observer) { return; } - UsingObserver us = new UsingObserver(observer, resource, disposer, eager); + UsingObserver us = new UsingObserver<>(observer, resource, disposer, eager); source.subscribe(us); } @@ -115,11 +115,9 @@ public void onError(Throwable t) { } } - upstream.dispose(); downstream.onError(t); } else { downstream.onError(t); - upstream.dispose(); disposeResource(); } } @@ -137,11 +135,9 @@ public void onComplete() { } } - upstream.dispose(); downstream.onComplete(); } else { downstream.onComplete(); - upstream.dispose(); disposeResource(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java index 08eb0327a6..c742ad6288 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindow.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,9 +36,9 @@ public ObservableWindow(ObservableSource source, long count, long skip, int c @Override public void subscribeActual(Observer> t) { if (count == skip) { - source.subscribe(new WindowExactObserver(t, count, capacityHint)); + source.subscribe(new WindowExactObserver<>(t, count, capacityHint)); } else { - source.subscribe(new WindowSkipObserver(t, count, skip, capacityHint)); + source.subscribe(new WindowSkipObserver<>(t, count, skip, capacityHint)); } } @@ -51,18 +51,20 @@ static final class WindowExactObserver final long count; final int capacityHint; + final AtomicBoolean cancelled; + long size; Disposable upstream; UnicastSubject window; - volatile boolean cancelled; - WindowExactObserver(Observer> actual, long count, int capacityHint) { this.downstream = actual; this.count = count; this.capacityHint = capacityHint; + this.cancelled = new AtomicBoolean(); + this.lazySet(1); } @Override @@ -77,21 +79,29 @@ public void onSubscribe(Disposable d) { @Override public void onNext(T t) { UnicastSubject w = window; - if (w == null && !cancelled) { + ObservableWindowSubscribeIntercept intercept = null; + if (w == null && !cancelled.get()) { + getAndIncrement(); + w = UnicastSubject.create(capacityHint, this); window = w; - downstream.onNext(w); + intercept = new ObservableWindowSubscribeIntercept<>(w); + downstream.onNext(intercept); } if (w != null) { w.onNext(t); + if (++size >= count) { size = 0; window = null; w.onComplete(); - if (cancelled) { - upstream.dispose(); - } + } + + if (intercept != null && intercept.tryAbandon()) { + window = null; + w.onComplete(); + w = null; } } } @@ -118,23 +128,25 @@ public void onComplete() { @Override public void dispose() { - cancelled = true; + if (cancelled.compareAndSet(false, true)) { + run(); + } } @Override public boolean isDisposed() { - return cancelled; + return cancelled.get(); } @Override public void run() { - if (cancelled) { + if (decrementAndGet() == 0) { upstream.dispose(); } } } - static final class WindowSkipObserver extends AtomicBoolean + static final class WindowSkipObserver extends AtomicInteger implements Observer, Disposable, Runnable { private static final long serialVersionUID = 3366976432059579510L; @@ -144,23 +156,23 @@ static final class WindowSkipObserver extends AtomicBoolean final int capacityHint; final ArrayDeque> windows; - long index; + final AtomicBoolean cancelled; - volatile boolean cancelled; + long index; /** Counts how many elements were emitted to the very first window in windows. */ long firstEmission; Disposable upstream; - final AtomicInteger wip = new AtomicInteger(); - WindowSkipObserver(Observer> actual, long count, long skip, int capacityHint) { this.downstream = actual; this.count = count; this.skip = skip; this.capacityHint = capacityHint; - this.windows = new ArrayDeque>(); + this.windows = new ArrayDeque<>(); + this.cancelled = new AtomicBoolean(); + this.lazySet(1); } @Override @@ -180,11 +192,14 @@ public void onNext(T t) { long s = skip; - if (i % s == 0 && !cancelled) { - wip.getAndIncrement(); + ObservableWindowSubscribeIntercept intercept = null; + + if (i % s == 0 && !cancelled.get()) { + getAndIncrement(); UnicastSubject w = UnicastSubject.create(capacityHint, this); + intercept = new ObservableWindowSubscribeIntercept<>(w); ws.offer(w); - downstream.onNext(w); + downstream.onNext(intercept); } long c = firstEmission + 1; @@ -195,8 +210,7 @@ public void onNext(T t) { if (c >= count) { ws.poll().onComplete(); - if (ws.isEmpty() && cancelled) { - this.upstream.dispose(); + if (ws.isEmpty() && cancelled.get()) { return; } firstEmission = c - s; @@ -205,6 +219,10 @@ public void onNext(T t) { } index = i + 1; + + if (intercept != null && intercept.tryAbandon()) { + intercept.window.onComplete(); + } } @Override @@ -227,20 +245,20 @@ public void onComplete() { @Override public void dispose() { - cancelled = true; + if (cancelled.compareAndSet(false, true)) { + run(); + } } @Override public boolean isDisposed() { - return cancelled; + return cancelled.get(); } @Override public void run() { - if (wip.decrementAndGet() == 0) { - if (cancelled) { - upstream.dispose(); - } + if (decrementAndGet() == 0) { + upstream.dispose(); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundary.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundary.java index cbcdfc3c91..a1ecb8e869 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundary.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundary.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public ObservableWindowBoundary(ObservableSource source, ObservableSource @Override public void subscribeActual(Observer> observer) { - WindowBoundaryMainObserver parent = new WindowBoundaryMainObserver(observer, capacityHint); + WindowBoundaryMainObserver parent = new WindowBoundaryMainObserver<>(observer, capacityHint); observer.onSubscribe(parent); other.subscribe(parent.boundaryObserver); @@ -75,10 +75,10 @@ static final class WindowBoundaryMainObserver WindowBoundaryMainObserver(Observer> downstream, int capacityHint) { this.downstream = downstream; this.capacityHint = capacityHint; - this.boundaryObserver = new WindowBoundaryInnerObserver(this); - this.upstream = new AtomicReference(); + this.boundaryObserver = new WindowBoundaryInnerObserver<>(this); + this.upstream = new AtomicReference<>(); this.windows = new AtomicInteger(1); - this.queue = new MpscLinkedQueue(); + this.queue = new MpscLinkedQueue<>(); this.errors = new AtomicThrowable(); this.stopWindows = new AtomicBoolean(); } @@ -230,7 +230,11 @@ void drain() { window = w; windows.getAndIncrement(); - downstream.onNext(w); + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(w); + downstream.onNext(intercept); + if (intercept.tryAbandon()) { + w.onComplete(); + } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundarySelector.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundarySelector.java index 35e92c3fec..cec435839e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundarySelector.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowBoundarySelector.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,63 +23,79 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.observers.QueueDrainObserver; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; -import io.reactivex.rxjava3.internal.util.NotificationLite; -import io.reactivex.rxjava3.observers.*; +import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.UnicastSubject; public final class ObservableWindowBoundarySelector extends AbstractObservableWithUpstream> { final ObservableSource open; - final Function> close; + final Function> closingIndicator; final int bufferSize; public ObservableWindowBoundarySelector( ObservableSource source, - ObservableSource open, Function> close, + ObservableSource open, Function> closingIndicator, int bufferSize) { super(source); this.open = open; - this.close = close; + this.closingIndicator = closingIndicator; this.bufferSize = bufferSize; } @Override public void subscribeActual(Observer> t) { - source.subscribe(new WindowBoundaryMainObserver( - new SerializedObserver>(t), - open, close, bufferSize)); + source.subscribe(new WindowBoundaryMainObserver<>( + t, open, closingIndicator, bufferSize)); } static final class WindowBoundaryMainObserver - extends QueueDrainObserver> - implements Disposable { + extends AtomicInteger + implements Observer, Disposable, Runnable { + private static final long serialVersionUID = 8646217640096099753L; + + final Observer> downstream; final ObservableSource open; - final Function> close; + final Function> closingIndicator; final int bufferSize; final CompositeDisposable resources; - Disposable upstream; + final WindowStartObserver startObserver; + + final List> windows; + + final SimplePlainQueue queue; + + final AtomicLong windowCount; - final AtomicReference boundary = new AtomicReference(); + final AtomicBoolean downstreamDisposed; - final List> ws; + final AtomicLong requested; + long emitted; - final AtomicLong windows = new AtomicLong(); + volatile boolean upstreamCanceled; - final AtomicBoolean stopWindows = new AtomicBoolean(); + volatile boolean upstreamDone; + volatile boolean openDone; + final AtomicThrowable error; - WindowBoundaryMainObserver(Observer> actual, - ObservableSource open, Function> close, int bufferSize) { - super(actual, new MpscLinkedQueue()); + Disposable upstream; + + WindowBoundaryMainObserver(Observer> downstream, + ObservableSource open, Function> closingIndicator, int bufferSize) { + this.downstream = downstream; + this.queue = new MpscLinkedQueue<>(); this.open = open; - this.close = close; + this.closingIndicator = closingIndicator; this.bufferSize = bufferSize; this.resources = new CompositeDisposable(); - this.ws = new ArrayList>(); - windows.lazySet(1); + this.windows = new ArrayList<>(); + this.windowCount = new AtomicLong(1L); + this.downstreamDisposed = new AtomicBoolean(); + this.error = new AtomicThrowable(); + this.startObserver = new WindowStartObserver<>(this); + this.requested = new AtomicLong(); } @Override @@ -89,281 +105,321 @@ public void onSubscribe(Disposable d) { downstream.onSubscribe(this); - if (stopWindows.get()) { - return; - } - - OperatorWindowBoundaryOpenObserver os = new OperatorWindowBoundaryOpenObserver(this); - - if (boundary.compareAndSet(null, os)) { - open.subscribe(os); - } + open.subscribe(startObserver); } } @Override public void onNext(T t) { - if (fastEnter()) { - for (UnicastSubject w : ws) { - w.onNext(t); - } - if (leave(-1) == 0) { - return; - } - } else { - queue.offer(NotificationLite.next(t)); - if (!enter()) { - return; - } - } - drainLoop(); + queue.offer(t); + drain(); } @Override public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; - } - error = t; - done = true; - - if (enter()) { - drainLoop(); - } - - if (windows.decrementAndGet() == 0) { - resources.dispose(); + startObserver.dispose(); + resources.dispose(); + if (error.tryAddThrowableOrReport(t)) { + upstreamDone = true; + drain(); } - - downstream.onError(t); } @Override public void onComplete() { - if (done) { - return; - } - done = true; - - if (enter()) { - drainLoop(); - } - - if (windows.decrementAndGet() == 0) { - resources.dispose(); - } - - downstream.onComplete(); - } - - void error(Throwable t) { - upstream.dispose(); + startObserver.dispose(); resources.dispose(); - onError(t); + upstreamDone = true; + drain(); } @Override public void dispose() { - if (stopWindows.compareAndSet(false, true)) { - DisposableHelper.dispose(boundary); - if (windows.decrementAndGet() == 0) { + if (downstreamDisposed.compareAndSet(false, true)) { + if (windowCount.decrementAndGet() == 0) { upstream.dispose(); + startObserver.dispose(); + resources.dispose(); + error.tryTerminateAndReport(); + upstreamCanceled = true; + drain(); + } else { + startObserver.dispose(); } } } @Override public boolean isDisposed() { - return stopWindows.get(); + return downstreamDisposed.get(); + } + + @Override + public void run() { + if (windowCount.decrementAndGet() == 0) { + upstream.dispose(); + startObserver.dispose(); + resources.dispose(); + error.tryTerminateAndReport(); + upstreamCanceled = true; + drain(); + } + } + + void open(B startValue) { + queue.offer(new WindowStartItem<>(startValue)); + drain(); } - void disposeBoundary() { + void openError(Throwable t) { + upstream.dispose(); resources.dispose(); - DisposableHelper.dispose(boundary); + if (error.tryAddThrowableOrReport(t)) { + upstreamDone = true; + drain(); + } } - void drainLoop() { - final MpscLinkedQueue q = (MpscLinkedQueue)queue; - final Observer> a = downstream; - final List> ws = this.ws; - int missed = 1; + void openComplete() { + openDone = true; + drain(); + } - for (;;) { + void close(WindowEndObserverIntercept what) { + queue.offer(what); + drain(); + } - for (;;) { - boolean d = done; + void closeError(Throwable t) { + upstream.dispose(); + startObserver.dispose(); + resources.dispose(); + if (error.tryAddThrowableOrReport(t)) { + upstreamDone = true; + drain(); + } + } - Object o = q.poll(); + void drain() { + if (getAndIncrement() != 0) { + return; + } - boolean empty = o == null; + int missed = 1; + final Observer> downstream = this.downstream; + final SimplePlainQueue queue = this.queue; + final List> windows = this.windows; - if (d && empty) { - disposeBoundary(); - Throwable e = error; - if (e != null) { - for (UnicastSubject w : ws) { - w.onError(e); - } - } else { - for (UnicastSubject w : ws) { - w.onComplete(); - } + for (;;) { + if (upstreamCanceled) { + queue.clear(); + windows.clear(); + } else { + boolean isDone = upstreamDone; + Object o = queue.poll(); + boolean isEmpty = o == null; + + if (isDone) { + if (isEmpty || error.get() != null) { + terminateDownstream(downstream); + upstreamCanceled = true; + continue; } - ws.clear(); - return; } - if (empty) { - break; - } + if (!isEmpty) { + if (o instanceof WindowStartItem) { + if (!downstreamDisposed.get()) { + @SuppressWarnings("unchecked") + B startItem = ((WindowStartItem)o).item; + + ObservableSource endSource; + try { + endSource = Objects.requireNonNull(closingIndicator.apply(startItem), "The closingIndicator returned a null ObservableSource"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + upstream.dispose(); + startObserver.dispose(); + resources.dispose(); + Exceptions.throwIfFatal(ex); + error.tryAddThrowableOrReport(ex); + upstreamDone = true; + continue; + } - if (o instanceof WindowOperation) { - @SuppressWarnings("unchecked") - WindowOperation wo = (WindowOperation) o; + windowCount.getAndIncrement(); + UnicastSubject newWindow = UnicastSubject.create(bufferSize, this); + WindowEndObserverIntercept endObserver = new WindowEndObserverIntercept<>(this, newWindow); - UnicastSubject w = wo.w; - if (w != null) { - if (ws.remove(wo.w)) { - wo.w.onComplete(); + downstream.onNext(endObserver); - if (windows.decrementAndGet() == 0) { - disposeBoundary(); - return; + if (endObserver.tryAbandon()) { + newWindow.onComplete(); + } else { + windows.add(newWindow); + resources.add(endObserver); + endSource.subscribe(endObserver); } } - continue; - } - - if (stopWindows.get()) { - continue; } + else if (o instanceof WindowEndObserverIntercept) { + @SuppressWarnings("unchecked") + UnicastSubject w = ((WindowEndObserverIntercept)o).window; - w = UnicastSubject.create(bufferSize); - - ws.add(w); - a.onNext(w); - - ObservableSource p; - - try { - p = ObjectHelper.requireNonNull(close.apply(wo.open), "The ObservableSource supplied is null"); - } catch (Throwable e) { - Exceptions.throwIfFatal(e); - stopWindows.set(true); - a.onError(e); - continue; - } - - OperatorWindowBoundaryCloseObserver cl = new OperatorWindowBoundaryCloseObserver(this, w); - - if (resources.add(cl)) { - windows.getAndIncrement(); + windows.remove(w); + resources.delete((Disposable)o); + w.onComplete(); + } else { + @SuppressWarnings("unchecked") + T item = (T)o; - p.subscribe(cl); + for (UnicastSubject w : windows) { + w.onNext(item); + } } continue; } - - for (UnicastSubject w : ws) { - w.onNext(NotificationLite.getValue(o)); + else if (openDone && windows.size() == 0) { + upstream.dispose(); + startObserver.dispose(); + resources.dispose(); + terminateDownstream(downstream); + upstreamCanceled = true; + continue; } } - missed = leave(-missed); + missed = addAndGet(-missed); if (missed == 0) { break; } } } - @Override - public void accept(Observer> a, Object v) { + void terminateDownstream(Observer downstream) { + Throwable ex = error.terminate(); + if (ex == null) { + for (UnicastSubject w : windows) { + w.onComplete(); + } + downstream.onComplete(); + } else if (ex != ExceptionHelper.TERMINATED) { + for (UnicastSubject w : windows) { + w.onError(ex); + } + downstream.onError(ex); + } } - void open(B b) { - queue.offer(new WindowOperation(null, b)); - if (enter()) { - drainLoop(); + static final class WindowStartItem { + + final B item; + + WindowStartItem(B item) { + this.item = item; } } - void close(OperatorWindowBoundaryCloseObserver w) { - resources.delete(w); - queue.offer(new WindowOperation(w.w, null)); - if (enter()) { - drainLoop(); + static final class WindowStartObserver extends AtomicReference + implements Observer { + + private static final long serialVersionUID = -3326496781427702834L; + + final WindowBoundaryMainObserver parent; + + WindowStartObserver(WindowBoundaryMainObserver parent) { + this.parent = parent; } - } - } - static final class WindowOperation { - final UnicastSubject w; - final B open; - WindowOperation(UnicastSubject w, B open) { - this.w = w; - this.open = open; - } - } + @Override + public void onSubscribe(Disposable d) { + DisposableHelper.setOnce(this, d); + } - static final class OperatorWindowBoundaryOpenObserver extends DisposableObserver { - final WindowBoundaryMainObserver parent; + @Override + public void onNext(B t) { + parent.open(t); + } - OperatorWindowBoundaryOpenObserver(WindowBoundaryMainObserver parent) { - this.parent = parent; - } + @Override + public void onError(Throwable t) { + parent.openError(t); + } - @Override - public void onNext(B t) { - parent.open(t); - } + @Override + public void onComplete() { + parent.openComplete(); + } - @Override - public void onError(Throwable t) { - parent.error(t); + void dispose() { + DisposableHelper.dispose(this); + } } - @Override - public void onComplete() { - parent.onComplete(); - } - } + static final class WindowEndObserverIntercept extends Observable + implements Observer, Disposable { - static final class OperatorWindowBoundaryCloseObserver extends DisposableObserver { - final WindowBoundaryMainObserver parent; - final UnicastSubject w; + final WindowBoundaryMainObserver parent; - boolean done; + final UnicastSubject window; - OperatorWindowBoundaryCloseObserver(WindowBoundaryMainObserver parent, UnicastSubject w) { - this.parent = parent; - this.w = w; - } + final AtomicReference upstream; - @Override - public void onNext(V t) { - dispose(); - onComplete(); - } + final AtomicBoolean once; - @Override - public void onError(Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; + WindowEndObserverIntercept(WindowBoundaryMainObserver parent, UnicastSubject window) { + this.parent = parent; + this.window = window; + this.upstream = new AtomicReference<>(); + this.once = new AtomicBoolean(); } - done = true; - parent.error(t); - } - @Override - public void onComplete() { - if (done) { - return; + @Override + public void onSubscribe(Disposable d) { + DisposableHelper.setOnce(upstream, d); + } + + @Override + public void onNext(V t) { + if (DisposableHelper.dispose(upstream)) { + parent.close(this); + } + } + + @Override + public void onError(Throwable t) { + if (isDisposed()) { + RxJavaPlugins.onError(t); + } else { + parent.closeError(t); + } + } + + @Override + public void onComplete() { + parent.close(this); + } + + @Override + public void dispose() { + DisposableHelper.dispose(upstream); + } + + @Override + public boolean isDisposed() { + return upstream.get() == DisposableHelper.DISPOSED; + } + + @Override + protected void subscribeActual(Observer o) { + window.subscribe(o); + once.set(true); + } + + boolean tryAbandon() { + return !once.get() && once.compareAndSet(false, true); } - done = true; - parent.close(this); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowSubscribeIntercept.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowSubscribeIntercept.java new file mode 100644 index 0000000000..4cc879662d --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowSubscribeIntercept.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import java.util.concurrent.atomic.AtomicBoolean; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.subjects.Subject; + +/** + * Wrapper for a Subject that detects an incoming subscriber. + * @param the element type of the flow. + * @since 3.0.0 + */ +final class ObservableWindowSubscribeIntercept extends Observable { + + final Subject window; + + final AtomicBoolean once; + + ObservableWindowSubscribeIntercept(Subject source) { + this.window = source; + this.once = new AtomicBoolean(); + } + + @Override + protected void subscribeActual(Observer s) { + window.subscribe(s); + once.set(true); + } + + boolean tryAbandon() { + return !once.get() && once.compareAndSet(false, true); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowTimed.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowTimed.java index cdd64cd6ae..2cc96a61a9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowTimed.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowTimed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,17 +15,16 @@ import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.*; -import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.core.Scheduler; import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.observers.QueueDrainObserver; import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; -import io.reactivex.rxjava3.internal.util.NotificationLite; -import io.reactivex.rxjava3.observers.SerializedObserver; +import io.reactivex.rxjava3.operators.SimplePlainQueue; import io.reactivex.rxjava3.subjects.UnicastSubject; public final class ObservableWindowTimed extends AbstractObservableWithUpstream> { @@ -37,8 +36,7 @@ public final class ObservableWindowTimed extends AbstractObservableWithUpstre final int bufferSize; final boolean restartTimerOnMaxSize; - public ObservableWindowTimed( - ObservableSource source, + public ObservableWindowTimed(Observable source, long timespan, long timeskip, TimeUnit unit, Scheduler scheduler, long maxSize, int bufferSize, boolean restartTimerOnMaxSize) { super(source); @@ -52,223 +50,282 @@ public ObservableWindowTimed( } @Override - public void subscribeActual(Observer> t) { - SerializedObserver> actual = new SerializedObserver>(t); - + protected void subscribeActual(Observer> downstream) { if (timespan == timeskip) { if (maxSize == Long.MAX_VALUE) { - source.subscribe(new WindowExactUnboundedObserver( - actual, + source.subscribe(new WindowExactUnboundedObserver<>( + downstream, timespan, unit, scheduler, bufferSize)); return; } - source.subscribe(new WindowExactBoundedObserver( - actual, + source.subscribe(new WindowExactBoundedObserver<>( + downstream, timespan, unit, scheduler, bufferSize, maxSize, restartTimerOnMaxSize)); return; } - source.subscribe(new WindowSkipObserver(actual, + source.subscribe(new WindowSkipObserver<>(downstream, timespan, timeskip, unit, scheduler.createWorker(), bufferSize)); } - static final class WindowExactUnboundedObserver - extends QueueDrainObserver> - implements Observer, Disposable, Runnable { + abstract static class AbstractWindowObserver + extends AtomicInteger + implements Observer, Disposable { + private static final long serialVersionUID = 5724293814035355511L; + + final Observer> downstream; + + final SimplePlainQueue queue; + final long timespan; final TimeUnit unit; - final Scheduler scheduler; final int bufferSize; - Disposable upstream; + long emitted; - UnicastSubject window; + volatile boolean done; + Throwable error; - final SequentialDisposable timer = new SequentialDisposable(); + Disposable upstream; - static final Object NEXT = new Object(); + final AtomicBoolean downstreamCancelled; - volatile boolean terminated; + volatile boolean upstreamCancelled; - WindowExactUnboundedObserver(Observer> actual, long timespan, TimeUnit unit, - Scheduler scheduler, int bufferSize) { - super(actual, new MpscLinkedQueue()); + final AtomicInteger windowCount; + + AbstractWindowObserver(Observer> downstream, long timespan, TimeUnit unit, int bufferSize) { + this.downstream = downstream; + this.queue = new MpscLinkedQueue<>(); this.timespan = timespan; this.unit = unit; - this.scheduler = scheduler; this.bufferSize = bufferSize; + this.downstreamCancelled = new AtomicBoolean(); + this.windowCount = new AtomicInteger(1); } @Override - public void onSubscribe(Disposable d) { + public final void onSubscribe(Disposable d) { if (DisposableHelper.validate(this.upstream, d)) { this.upstream = d; - window = UnicastSubject.create(bufferSize); - - Observer> a = downstream; - a.onSubscribe(this); - - a.onNext(window); + downstream.onSubscribe(this); - if (!cancelled) { - Disposable task = scheduler.schedulePeriodicallyDirect(this, timespan, timespan, unit); - timer.replace(task); - } + createFirstWindow(); } } + abstract void createFirstWindow(); + @Override - public void onNext(T t) { - if (terminated) { - return; - } - if (fastEnter()) { - window.onNext(t); - if (leave(-1) == 0) { - return; - } - } else { - queue.offer(NotificationLite.next(t)); - if (!enter()) { - return; - } - } - drainLoop(); + public final void onNext(T t) { + queue.offer(t); + drain(); } @Override - public void onError(Throwable t) { + public final void onError(Throwable t) { error = t; done = true; - if (enter()) { - drainLoop(); - } - - downstream.onError(t); + drain(); } @Override - public void onComplete() { + public final void onComplete() { done = true; - if (enter()) { - drainLoop(); - } + drain(); + } - downstream.onComplete(); + @Override + public final void dispose() { + if (downstreamCancelled.compareAndSet(false, true)) { + windowDone(); + } } @Override - public void dispose() { - cancelled = true; + public final boolean isDisposed() { + return downstreamCancelled.get(); + } + + final void windowDone() { + if (windowCount.decrementAndGet() == 0) { + cleanupResources(); + upstream.dispose(); + upstreamCancelled = true; + drain(); + } + } + + abstract void cleanupResources(); + + abstract void drain(); + } + + static final class WindowExactUnboundedObserver + extends AbstractWindowObserver + implements Runnable { + + private static final long serialVersionUID = 1155822639622580836L; + + final Scheduler scheduler; + + UnicastSubject window; + + final SequentialDisposable timer; + + static final Object NEXT_WINDOW = new Object(); + + final Runnable windowRunnable; + + WindowExactUnboundedObserver(Observer> actual, long timespan, TimeUnit unit, + Scheduler scheduler, int bufferSize) { + super(actual, timespan, unit, bufferSize); + this.scheduler = scheduler; + this.timer = new SequentialDisposable(); + this.windowRunnable = new WindowRunnable(); } @Override - public boolean isDisposed() { - return cancelled; + void createFirstWindow() { + if (!downstreamCancelled.get()) { + windowCount.getAndIncrement(); + window = UnicastSubject.create(bufferSize, windowRunnable); + + emitted = 1; + + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(window); + downstream.onNext(intercept); + + timer.replace(scheduler.schedulePeriodicallyDirect(this, timespan, timespan, unit)); + + if (intercept.tryAbandon()) { + window.onComplete(); + } + } } @Override public void run() { - if (cancelled) { - terminated = true; - } - queue.offer(NEXT); - if (enter()) { - drainLoop(); - } + queue.offer(NEXT_WINDOW); + drain(); } - void drainLoop() { + @Override + void drain() { + if (getAndIncrement() != 0) { + return; + } - final MpscLinkedQueue q = (MpscLinkedQueue)queue; - final Observer> a = downstream; - UnicastSubject w = window; + final SimplePlainQueue queue = this.queue; + final Observer> downstream = this.downstream; + UnicastSubject window = this.window; int missed = 1; for (;;) { - for (;;) { - boolean term = terminated; // NOPMD - - boolean d = done; - - Object o = q.poll(); - - if (d && (o == null || o == NEXT)) { - window = null; - q.clear(); - Throwable err = error; - if (err != null) { - w.onError(err); + if (upstreamCancelled) { + queue.clear(); + window = null; + this.window = null; + } else { + boolean isDone = done; + Object o = queue.poll(); + boolean isEmpty = o == null; + + if (isDone && isEmpty) { + Throwable ex = error; + if (ex != null) { + if (window != null) { + window.onError(ex); + } + downstream.onError(ex); } else { - w.onComplete(); + if (window != null) { + window.onComplete(); + } + downstream.onComplete(); } - timer.dispose(); - return; + cleanupResources(); + upstreamCancelled = true; + continue; } + else if (!isEmpty) { - if (o == null) { - break; - } + if (o == NEXT_WINDOW) { + if (window != null) { + window.onComplete(); + window = null; + this.window = null; + } + if (downstreamCancelled.get()) { + timer.dispose(); + } else { + emitted++; - if (o == NEXT) { - w.onComplete(); - if (!term) { - w = UnicastSubject.create(bufferSize); - window = w; + windowCount.getAndIncrement(); + window = UnicastSubject.create(bufferSize, windowRunnable); + this.window = window; - a.onNext(w); - } else { - upstream.dispose(); + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(window); + downstream.onNext(intercept); + + if (intercept.tryAbandon()) { + window.onComplete(); + } + } + } else if (window != null) { + @SuppressWarnings("unchecked") + T item = (T)o; + window.onNext(item); } + continue; } - - w.onNext(NotificationLite.getValue(o)); } - missed = leave(-missed); + missed = addAndGet(-missed); if (missed == 0) { break; } } } + + @Override + void cleanupResources() { + timer.dispose(); + } + + final class WindowRunnable implements Runnable { + @Override + public void run() { + windowDone(); + } + } } static final class WindowExactBoundedObserver - extends QueueDrainObserver> - implements Disposable { - final long timespan; - final TimeUnit unit; + extends AbstractWindowObserver + implements Runnable { + private static final long serialVersionUID = -6130475889925953722L; + final Scheduler scheduler; - final int bufferSize; final boolean restartTimerOnMaxSize; final long maxSize; - final Scheduler.Worker worker; long count; - long producerIndex; - - Disposable upstream; - UnicastSubject window; - volatile boolean terminated; - - final SequentialDisposable timer = new SequentialDisposable(); + final SequentialDisposable timer; WindowExactBoundedObserver( Observer> actual, long timespan, TimeUnit unit, Scheduler scheduler, int bufferSize, long maxSize, boolean restartTimerOnMaxSize) { - super(actual, new MpscLinkedQueue()); - this.timespan = timespan; - this.unit = unit; + super(actual, timespan, unit, bufferSize); this.scheduler = scheduler; - this.bufferSize = bufferSize; this.maxSize = maxSize; this.restartTimerOnMaxSize = restartTimerOnMaxSize; if (restartTimerOnMaxSize) { @@ -276,415 +333,288 @@ static final class WindowExactBoundedObserver } else { worker = null; } + this.timer = new SequentialDisposable(); } @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(this.upstream, d)) { - this.upstream = d; - - Observer> a = downstream; - - a.onSubscribe(this); - - if (cancelled) { - return; - } + void createFirstWindow() { + if (!downstreamCancelled.get()) { + emitted = 1; - UnicastSubject w = UnicastSubject.create(bufferSize); - window = w; + windowCount.getAndIncrement(); + window = UnicastSubject.create(bufferSize, this); - a.onNext(w); + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(window); + downstream.onNext(intercept); - Disposable task; - ConsumerIndexHolder consumerIndexHolder = new ConsumerIndexHolder(producerIndex, this); + Runnable boundaryTask = new WindowBoundaryRunnable(this, 1L); if (restartTimerOnMaxSize) { - task = worker.schedulePeriodically(consumerIndexHolder, timespan, timespan, unit); + timer.replace(worker.schedulePeriodically(boundaryTask, timespan, timespan, unit)); } else { - task = scheduler.schedulePeriodicallyDirect(consumerIndexHolder, timespan, timespan, unit); + timer.replace(scheduler.schedulePeriodicallyDirect(boundaryTask, timespan, timespan, unit)); } - timer.replace(task); - } - } - - @Override - public void onNext(T t) { - if (terminated) { - return; - } - - if (fastEnter()) { - UnicastSubject w = window; - w.onNext(t); - - long c = count + 1; - - if (c >= maxSize) { - producerIndex++; - count = 0; - - w.onComplete(); - - w = UnicastSubject.create(bufferSize); - window = w; - downstream.onNext(w); - if (restartTimerOnMaxSize) { - Disposable tm = timer.get(); - tm.dispose(); - Disposable task = worker.schedulePeriodically( - new ConsumerIndexHolder(producerIndex, this), timespan, timespan, unit); - - DisposableHelper.replace(timer, task); - } - } else { - count = c; - } - - if (leave(-1) == 0) { - return; - } - } else { - queue.offer(NotificationLite.next(t)); - if (!enter()) { - return; + if (intercept.tryAbandon()) { + window.onComplete(); } } - drainLoop(); } @Override - public void onError(Throwable t) { - error = t; - done = true; - if (enter()) { - drainLoop(); - } - - downstream.onError(t); + public void run() { + windowDone(); } @Override - public void onComplete() { - done = true; - if (enter()) { - drainLoop(); + void cleanupResources() { + timer.dispose(); + Worker w = worker; + if (w != null) { + w.dispose(); } - - downstream.onComplete(); } - @Override - public void dispose() { - cancelled = true; + void boundary(WindowBoundaryRunnable sender) { + queue.offer(sender); + drain(); } @Override - public boolean isDisposed() { - return cancelled; - } - - void disposeTimer() { - DisposableHelper.dispose(timer); - Worker w = worker; - if (w != null) { - w.dispose(); + void drain() { + if (getAndIncrement() != 0) { + return; } - } - - void drainLoop() { - final MpscLinkedQueue q = (MpscLinkedQueue)queue; - final Observer> a = downstream; - UnicastSubject w = window; int missed = 1; - for (;;) { + final SimplePlainQueue queue = this.queue; + final Observer> downstream = this.downstream; + UnicastSubject window = this.window; - for (;;) { - if (terminated) { - upstream.dispose(); - q.clear(); - disposeTimer(); - return; - } - - boolean d = done; + for (;;) { - Object o = q.poll(); + if (upstreamCancelled) { + queue.clear(); + window = null; + this.window = null; + } else { - boolean empty = o == null; - boolean isHolder = o instanceof ConsumerIndexHolder; + boolean isDone = done; + Object o = queue.poll(); + boolean isEmpty = o == null; - if (d && (empty || isHolder)) { - window = null; - q.clear(); - Throwable err = error; - if (err != null) { - w.onError(err); + if (isDone && isEmpty) { + Throwable ex = error; + if (ex != null) { + if (window != null) { + window.onError(ex); + } + downstream.onError(ex); } else { - w.onComplete(); + if (window != null) { + window.onComplete(); + } + downstream.onComplete(); } - disposeTimer(); - return; - } - - if (empty) { - break; - } - - if (isHolder) { - ConsumerIndexHolder consumerIndexHolder = (ConsumerIndexHolder) o; - if (!restartTimerOnMaxSize || producerIndex == consumerIndexHolder.index) { - w.onComplete(); - count = 0; - w = UnicastSubject.create(bufferSize); - window = w; - - a.onNext(w); + cleanupResources(); + upstreamCancelled = true; + continue; + } else if (!isEmpty) { + if (o instanceof WindowBoundaryRunnable) { + WindowBoundaryRunnable boundary = (WindowBoundaryRunnable) o; + if (boundary.index == emitted || !restartTimerOnMaxSize) { + this.count = 0; + window = createNewWindow(window); + } + } else if (window != null) { + @SuppressWarnings("unchecked") + T item = (T)o; + window.onNext(item); + + long count = this.count + 1; + if (count == maxSize) { + this.count = 0; + window = createNewWindow(window); + } else { + this.count = count; + } } + continue; } + } - w.onNext(NotificationLite.getValue(o)); - long c = count + 1; - - if (c >= maxSize) { - producerIndex++; - count = 0; + missed = addAndGet(-missed); + if (missed == 0) { + break; + } + } + } - w.onComplete(); + UnicastSubject createNewWindow(UnicastSubject window) { + if (window != null) { + window.onComplete(); + window = null; + } - w = UnicastSubject.create(bufferSize); - window = w; - downstream.onNext(w); + if (downstreamCancelled.get()) { + cleanupResources(); + } else { + long emitted = this.emitted; + this.emitted = ++emitted; - if (restartTimerOnMaxSize) { - Disposable tm = timer.get(); - tm.dispose(); + windowCount.getAndIncrement(); + window = UnicastSubject.create(bufferSize, this); + this.window = window; - Disposable task = worker.schedulePeriodically( - new ConsumerIndexHolder(producerIndex, this), timespan, timespan, unit); - if (!timer.compareAndSet(tm, task)) { - task.dispose(); - } - } + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(window); + downstream.onNext(intercept); - } else { - count = c; - } + if (restartTimerOnMaxSize) { + timer.update(worker.schedulePeriodically(new WindowBoundaryRunnable(this, emitted), timespan, timespan, unit)); } - missed = leave(-missed); - if (missed == 0) { - break; + if (intercept.tryAbandon()) { + window.onComplete(); } } + + return window; } - static final class ConsumerIndexHolder implements Runnable { - final long index; + static final class WindowBoundaryRunnable implements Runnable { + final WindowExactBoundedObserver parent; - ConsumerIndexHolder(long index, WindowExactBoundedObserver parent) { - this.index = index; + + final long index; + + WindowBoundaryRunnable(WindowExactBoundedObserver parent, long index) { this.parent = parent; + this.index = index; } @Override public void run() { - WindowExactBoundedObserver p = parent; - - if (!p.cancelled) { - p.queue.offer(this); - } else { - p.terminated = true; - } - if (p.enter()) { - p.drainLoop(); - } + parent.boundary(this); } } } static final class WindowSkipObserver - extends QueueDrainObserver> - implements Disposable, Runnable { - final long timespan; + extends AbstractWindowObserver + implements Runnable { + private static final long serialVersionUID = -7852870764194095894L; + final long timeskip; - final TimeUnit unit; final Scheduler.Worker worker; - final int bufferSize; final List> windows; - Disposable upstream; - - volatile boolean terminated; - WindowSkipObserver(Observer> actual, long timespan, long timeskip, TimeUnit unit, Worker worker, int bufferSize) { - super(actual, new MpscLinkedQueue()); - this.timespan = timespan; + super(actual, timespan, unit, bufferSize); this.timeskip = timeskip; - this.unit = unit; this.worker = worker; - this.bufferSize = bufferSize; - this.windows = new LinkedList>(); + this.windows = new LinkedList<>(); } @Override - public void onSubscribe(Disposable d) { - if (DisposableHelper.validate(this.upstream, d)) { - this.upstream = d; - - downstream.onSubscribe(this); - - if (cancelled) { - return; - } - - final UnicastSubject w = UnicastSubject.create(bufferSize); - windows.add(w); + void createFirstWindow() { + if (!downstreamCancelled.get()) { + emitted = 1; - downstream.onNext(w); - worker.schedule(new CompletionTask(w), timespan, unit); + windowCount.getAndIncrement(); + UnicastSubject window = UnicastSubject.create(bufferSize, this); + windows.add(window); - worker.schedulePeriodically(this, timeskip, timeskip, unit); - } + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(window); + downstream.onNext(intercept); - } + worker.schedule(new WindowBoundaryRunnable(this, false), timespan, unit); + worker.schedulePeriodically(new WindowBoundaryRunnable(this, true), timeskip, timeskip, unit); - @Override - public void onNext(T t) { - if (fastEnter()) { - for (UnicastSubject w : windows) { - w.onNext(t); - } - if (leave(-1) == 0) { - return; + if (intercept.tryAbandon()) { + window.onComplete(); + windows.remove(window); } - } else { - queue.offer(t); - if (!enter()) { - return; - } - } - drainLoop(); - } - - @Override - public void onError(Throwable t) { - error = t; - done = true; - if (enter()) { - drainLoop(); } - - downstream.onError(t); - } - - @Override - public void onComplete() { - done = true; - if (enter()) { - drainLoop(); - } - - downstream.onComplete(); } @Override - public void dispose() { - cancelled = true; + void cleanupResources() { + worker.dispose(); } @Override - public boolean isDisposed() { - return cancelled; - } - - void complete(UnicastSubject w) { - queue.offer(new SubjectWork(w, false)); - if (enter()) { - drainLoop(); + void drain() { + if (getAndIncrement() != 0) { + return; } - } - - @SuppressWarnings("unchecked") - void drainLoop() { - final MpscLinkedQueue q = (MpscLinkedQueue)queue; - final Observer> a = downstream; - final List> ws = windows; int missed = 1; + final SimplePlainQueue queue = this.queue; + final Observer> downstream = this.downstream; + final List> windows = this.windows; for (;;) { - - for (;;) { - if (terminated) { - upstream.dispose(); - q.clear(); - ws.clear(); - worker.dispose(); - return; - } - - boolean d = done; - - Object v = q.poll(); - - boolean empty = v == null; - boolean sw = v instanceof SubjectWork; - - if (d && (empty || sw)) { - q.clear(); - Throwable e = error; - if (e != null) { - for (UnicastSubject w : ws) { - w.onError(e); + if (upstreamCancelled) { + queue.clear(); + windows.clear(); + } else { + boolean isDone = done; + Object o = queue.poll(); + boolean isEmpty = o == null; + + if (isDone && isEmpty) { + Throwable ex = error; + if (ex != null) { + for (UnicastSubject window : windows) { + window.onError(ex); } + downstream.onError(ex); } else { - for (UnicastSubject w : ws) { - w.onComplete(); + for (UnicastSubject window : windows) { + window.onComplete(); } + downstream.onComplete(); } - ws.clear(); - worker.dispose(); - return; - } + cleanupResources(); + upstreamCancelled = true; + continue; + } else if (!isEmpty) { + if (o == WINDOW_OPEN) { + if (!downstreamCancelled.get()) { + long emitted = this.emitted; + this.emitted = ++emitted; - if (empty) { - break; - } + windowCount.getAndIncrement(); + UnicastSubject window = UnicastSubject.create(bufferSize, this); + windows.add(window); - if (sw) { - SubjectWork work = (SubjectWork)v; + ObservableWindowSubscribeIntercept intercept = new ObservableWindowSubscribeIntercept<>(window); + downstream.onNext(intercept); - if (work.open) { - if (cancelled) { - continue; - } + worker.schedule(new WindowBoundaryRunnable(this, false), timespan, unit); - final UnicastSubject w = UnicastSubject.create(bufferSize); - ws.add(w); - a.onNext(w); - - worker.schedule(new CompletionTask(w), timespan, unit); + if (intercept.tryAbandon()) { + window.onComplete(); + } + } + } else if (o == WINDOW_CLOSE) { + if (!windows.isEmpty()) { + windows.remove(0).onComplete(); + } } else { - ws.remove(work.w); - work.w.onComplete(); - if (ws.isEmpty() && cancelled) { - terminated = true; + @SuppressWarnings("unchecked") + T item = (T)o; + for (UnicastSubject window : windows) { + window.onNext(item); } } - } else { - for (UnicastSubject w : ws) { - w.onNext((T)v); - } + continue; } } - - missed = leave(-missed); + missed = addAndGet(-missed); if (missed == 0) { break; } @@ -693,37 +623,31 @@ void drainLoop() { @Override public void run() { - - UnicastSubject w = UnicastSubject.create(bufferSize); - - SubjectWork sw = new SubjectWork(w, true); - if (!cancelled) { - queue.offer(sw); - } - if (enter()) { - drainLoop(); - } + windowDone(); } - static final class SubjectWork { - final UnicastSubject w; - final boolean open; - SubjectWork(UnicastSubject w, boolean open) { - this.w = w; - this.open = open; - } + void boundary(boolean isOpen) { + queue.offer(isOpen ? WINDOW_OPEN : WINDOW_CLOSE); + drain(); } - final class CompletionTask implements Runnable { - private final UnicastSubject w; + static final Object WINDOW_OPEN = new Object(); + static final Object WINDOW_CLOSE = new Object(); + + static final class WindowBoundaryRunnable implements Runnable { - CompletionTask(UnicastSubject w) { - this.w = w; + final WindowSkipObserver parent; + + final boolean isOpen; + + WindowBoundaryRunnable(WindowSkipObserver parent, boolean isOpen) { + this.parent = parent; + this.isOpen = isOpen; } @Override public void run() { - complete(w); + parent.boundary(isOpen); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFrom.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFrom.java index 91b4b0266d..95b21187a7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFrom.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFrom.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.observable; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.observers.SerializedObserver; public final class ObservableWithLatestFrom extends AbstractObservableWithUpstream { @@ -35,8 +35,8 @@ public ObservableWithLatestFrom(ObservableSource source, @Override public void subscribeActual(Observer t) { - final SerializedObserver serial = new SerializedObserver(t); - final WithLatestFromObserver wlf = new WithLatestFromObserver(serial, combiner); + final SerializedObserver serial = new SerializedObserver<>(t); + final WithLatestFromObserver wlf = new WithLatestFromObserver<>(serial, combiner); serial.onSubscribe(wlf); @@ -53,9 +53,9 @@ static final class WithLatestFromObserver extends AtomicReference im final BiFunction combiner; - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); - final AtomicReference other = new AtomicReference(); + final AtomicReference other = new AtomicReference<>(); WithLatestFromObserver(Observer actual, BiFunction combiner) { this.downstream = actual; @@ -73,7 +73,7 @@ public void onNext(T t) { if (u != null) { R r; try { - r = ObjectHelper.requireNonNull(combiner.apply(t, u), "The combiner returned a null value"); + r = Objects.requireNonNull(combiner.apply(t, u), "The combiner returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); dispose(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromMany.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromMany.java index fc64adc42f..f8327b8284 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromMany.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromMany.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,9 +10,11 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.annotations.*; @@ -21,7 +23,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -82,11 +83,11 @@ protected void subscribeActual(Observer observer) { } if (n == 0) { - new ObservableMap(source, new SingletonArrayFunc()).subscribeActual(observer); + new ObservableMap<>(source, new SingletonArrayFunc()).subscribeActual(observer); return; } - WithLatestFromObserver parent = new WithLatestFromObserver(observer, combiner, n); + WithLatestFromObserver parent = new WithLatestFromObserver<>(observer, combiner, n); observer.onSubscribe(parent); parent.subscribe(others, n); @@ -121,8 +122,8 @@ static final class WithLatestFromObserver s[i] = new WithLatestInnerObserver(this, i); } this.observers = s; - this.values = new AtomicReferenceArray(n); - this.upstream = new AtomicReference(); + this.values = new AtomicReferenceArray<>(n); + this.upstream = new AtomicReference<>(); this.error = new AtomicThrowable(); } @@ -164,7 +165,7 @@ public void onNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(combiner.apply(objects), "combiner returned a null value"); + v = Objects.requireNonNull(combiner.apply(objects), "combiner returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); dispose(); @@ -285,7 +286,7 @@ public void dispose() { final class SingletonArrayFunc implements Function { @Override public R apply(T t) throws Throwable { - return ObjectHelper.requireNonNull(combiner.apply(new Object[] { t }), "The combiner returned a null value"); + return Objects.requireNonNull(combiner.apply(new Object[] { t }), "The combiner returned a null value"); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZip.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZip.java index b3d40dc2ed..e1626b4865 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZip.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZip.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -21,8 +22,7 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; public final class ObservableZip extends Observable { @@ -68,7 +68,7 @@ public void subscribeActual(Observer observer) { return; } - ZipCoordinator zc = new ZipCoordinator(observer, zipper, count, delayError); + ZipCoordinator zc = new ZipCoordinator<>(observer, zipper, count, delayError); zc.subscribe(sources, bufferSize); } @@ -98,7 +98,7 @@ public void subscribe(ObservableSource[] sources, int bufferSize) { ZipObserver[] s = observers; int len = s.length; for (int i = 0; i < len; i++) { - s[i] = new ZipObserver(this, bufferSize); + s[i] = new ZipObserver<>(this, bufferSize); } // this makes sure the contents of the observers array is visible this.lazySet(0); @@ -195,7 +195,7 @@ public void drain() { R v; try { - v = ObjectHelper.requireNonNull(zipper.apply(os.clone()), "The zipper returned a null value"); + v = Objects.requireNonNull(zipper.apply(os.clone()), "The zipper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancel(); @@ -263,11 +263,11 @@ static final class ZipObserver implements Observer { volatile boolean done; Throwable error; - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); ZipObserver(ZipCoordinator parent, int bufferSize) { this.parent = parent; - this.queue = new SpscLinkedArrayQueue(bufferSize); + this.queue = new SpscLinkedArrayQueue<>(bufferSize); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java index d86b90395d..7c8f5e6d0e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,13 +14,13 @@ package io.reactivex.rxjava3.internal.operators.observable; import java.util.Iterator; +import java.util.Objects; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class ObservableZipIterable extends Observable { @@ -41,7 +41,7 @@ public void subscribeActual(Observer t) { Iterator it; try { - it = ObjectHelper.requireNonNull(other.iterator(), "The iterator returned by other is null"); + it = Objects.requireNonNull(other.iterator(), "The iterator returned by other is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptyDisposable.error(e, t); @@ -109,19 +109,19 @@ public void onNext(T t) { U u; try { - u = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + u = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); - error(e); + fail(e); return; } V v; try { - v = ObjectHelper.requireNonNull(zipper.apply(t, u), "The zipper function returned a null value"); + v = Objects.requireNonNull(zipper.apply(t, u), "The zipper function returned a null value"); } catch (Throwable e) { Exceptions.throwIfFatal(e); - error(e); + fail(e); return; } @@ -133,7 +133,7 @@ public void onNext(T t) { b = iterator.hasNext(); } catch (Throwable e) { Exceptions.throwIfFatal(e); - error(e); + fail(e); return; } @@ -144,7 +144,7 @@ public void onNext(T t) { } } - void error(Throwable e) { + void fail(Throwable e) { done = true; upstream.dispose(); downstream.onError(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObserverResourceWrapper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObserverResourceWrapper.java index 4111a36af4..a3c797fee6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObserverResourceWrapper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/observable/ObserverResourceWrapper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public final class ObserverResourceWrapper extends AtomicReference downstream; - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); public ObserverResourceWrapper(Observer downstream) { this.downstream = downstream; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelCollect.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelCollect.java index d37bcfa1b3..60e36beed4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelCollect.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelCollect.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,12 +17,13 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscribers.DeferredScalarSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduce the sequence of values in each 'rail' to a single value. * @@ -46,6 +47,8 @@ public ParallelCollect(ParallelFlowable source, @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -59,14 +62,14 @@ public void subscribe(Subscriber[] subscribers) { C initialValue; try { - initialValue = ObjectHelper.requireNonNull(initialCollection.get(), "The initialSupplier returned a null value"); + initialValue = Objects.requireNonNull(initialCollection.get(), "The initialSupplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); reportError(subscribers, ex); return; } - parents[i] = new ParallelCollectSubscriber(subscribers[i], initialValue, collector); + parents[i] = new ParallelCollectSubscriber<>(subscribers[i], initialValue, collector); } source.subscribe(parents); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelConcatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelConcatMap.java index 5b7d3ef5c4..ebdcf0bdf3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelConcatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelConcatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,10 +16,12 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +import java.util.Objects; /** * Concatenates the generated Publishers on each rail. @@ -42,9 +44,9 @@ public ParallelConcatMap( Function> mapper, int prefetch, ErrorMode errorMode) { this.source = source; - this.mapper = ObjectHelper.requireNonNull(mapper, "mapper"); + this.mapper = Objects.requireNonNull(mapper, "mapper"); this.prefetch = prefetch; - this.errorMode = ObjectHelper.requireNonNull(errorMode, "errorMode"); + this.errorMode = Objects.requireNonNull(errorMode, "errorMode"); } @Override @@ -54,6 +56,8 @@ public int parallelism() { @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelDoOnNextTry.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelDoOnNextTry.java index 0e93abcccb..b8ff388535 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelDoOnNextTry.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelDoOnNextTry.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,12 +17,13 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.parallel.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Calls a Consumer for each upstream value passing by * and handles any failure with a handler function. @@ -47,6 +48,8 @@ public ParallelDoOnNextTry(ParallelFlowable source, Consumer onNex @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -58,9 +61,9 @@ public void subscribe(Subscriber[] subscribers) { for (int i = 0; i < n; i++) { Subscriber a = subscribers[i]; if (a instanceof ConditionalSubscriber) { - parents[i] = new ParallelDoOnNextConditionalSubscriber((ConditionalSubscriber)a, onNext, errorHandler); + parents[i] = new ParallelDoOnNextConditionalSubscriber<>((ConditionalSubscriber)a, onNext, errorHandler); } else { - parents[i] = new ParallelDoOnNextSubscriber(a, onNext, errorHandler); + parents[i] = new ParallelDoOnNextSubscriber<>(a, onNext, errorHandler); } } @@ -133,7 +136,7 @@ public boolean tryOnNext(T t) { ParallelFailureHandling h; try { - h = ObjectHelper.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null item"); + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); } catch (Throwable exc) { Exceptions.throwIfFatal(exc); cancel(); @@ -244,7 +247,7 @@ public boolean tryOnNext(T t) { ParallelFailureHandling h; try { - h = ObjectHelper.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null item"); + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); } catch (Throwable exc) { Exceptions.throwIfFatal(exc); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilter.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilter.java index 6d1fc2fb96..28987a6fc9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilter.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,8 +17,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Predicate; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -40,6 +40,8 @@ public ParallelFilter(ParallelFlowable source, Predicate predicate @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -51,9 +53,9 @@ public void subscribe(Subscriber[] subscribers) { for (int i = 0; i < n; i++) { Subscriber a = subscribers[i]; if (a instanceof ConditionalSubscriber) { - parents[i] = new ParallelFilterConditionalSubscriber((ConditionalSubscriber)a, predicate); + parents[i] = new ParallelFilterConditionalSubscriber<>((ConditionalSubscriber)a, predicate); } else { - parents[i] = new ParallelFilterSubscriber(a, predicate); + parents[i] = new ParallelFilterSubscriber<>(a, predicate); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilterTry.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilterTry.java index 12366c30fa..563937e1a6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilterTry.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFilterTry.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,12 +17,13 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.parallel.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Filters each 'rail' of the source ParallelFlowable with a predicate function. * @@ -45,6 +46,8 @@ public ParallelFilterTry(ParallelFlowable source, Predicate predic @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -56,9 +59,9 @@ public void subscribe(Subscriber[] subscribers) { for (int i = 0; i < n; i++) { Subscriber a = subscribers[i]; if (a instanceof ConditionalSubscriber) { - parents[i] = new ParallelFilterConditionalSubscriber((ConditionalSubscriber)a, predicate, errorHandler); + parents[i] = new ParallelFilterConditionalSubscriber<>((ConditionalSubscriber)a, predicate, errorHandler); } else { - parents[i] = new ParallelFilterSubscriber(a, predicate, errorHandler); + parents[i] = new ParallelFilterSubscriber<>(a, predicate, errorHandler); } } @@ -136,7 +139,7 @@ public boolean tryOnNext(T t) { ParallelFailureHandling h; try { - h = ObjectHelper.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null item"); + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); } catch (Throwable exc) { Exceptions.throwIfFatal(exc); cancel(); @@ -225,7 +228,7 @@ public boolean tryOnNext(T t) { ParallelFailureHandling h; try { - h = ObjectHelper.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null item"); + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); } catch (Throwable exc) { Exceptions.throwIfFatal(exc); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMap.java index 1e0c9f5738..227521143a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,6 +18,7 @@ import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.operators.flowable.FlowableFlatMap; import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * Flattens the generated Publishers on each rail. @@ -57,6 +58,8 @@ public int parallelism() { @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMapIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMapIterable.java new file mode 100644 index 0000000000..9e6c45a9ca --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFlatMapIterable.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.parallel; + +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableFlattenIterable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Flattens the generated {@link Iterable}s on each rail. + * + * @param the input value type + * @param the output value type + * @since 3.0.0 + */ +public final class ParallelFlatMapIterable extends ParallelFlowable { + + final ParallelFlowable source; + + final Function> mapper; + + final int prefetch; + + public ParallelFlatMapIterable( + ParallelFlowable source, + Function> mapper, + int prefetch) { + this.source = source; + this.mapper = mapper; + this.prefetch = prefetch; + } + + @Override + public int parallelism() { + return source.parallelism(); + } + + @Override + public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + + if (!validate(subscribers)) { + return; + } + + int n = subscribers.length; + + @SuppressWarnings("unchecked") + final Subscriber[] parents = new Subscriber[n]; + + for (int i = 0; i < n; i++) { + parents[i] = FlowableFlattenIterable.subscribe(subscribers[i], mapper, prefetch); + } + + source.subscribe(parents); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromArray.java index cb40caaad5..d2512ccd7f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * Wraps multiple Publishers into a ParallelFlowable which runs them @@ -37,6 +38,8 @@ public int parallelism() { @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java index 126f7cdf13..eb57bccea5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelFromPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,11 +19,13 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * Dispatches the values from upstream in a round robin fashion to subscribers which are @@ -51,11 +53,13 @@ public int parallelism() { @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } - source.subscribe(new ParallelDispatcher(subscribers, prefetch)); + source.subscribe(new ParallelDispatcher<>(subscribers, prefetch)); } static final class ParallelDispatcher @@ -137,7 +141,7 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(prefetch); + queue = new SpscArrayQueue<>(prefetch); setupSubscribers(); @@ -150,10 +154,6 @@ void setupSubscribers() { final int m = subs.length; for (int i = 0; i < m; i++) { - if (cancelled) { - return; - } - subscriberCount.lazySet(i + 1); subs[i].onSubscribe(new RailSubscription(i, m)); @@ -204,7 +204,7 @@ public void onNext(T t) { if (sourceMode == QueueSubscription.NONE) { if (!queue.offer(t)) { upstream.cancel(); - onError(new MissingBackpressureException("Queue is full?")); + onError(new QueueOverflowException()); return; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java index b15fa8dfd5..2852fda8c7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelJoin.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,12 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -50,9 +51,9 @@ public ParallelJoin(ParallelFlowable source, int prefetch, boolean protected void subscribeActual(Subscriber s) { JoinSubscriptionBase parent; if (delayErrors) { - parent = new JoinSubscriptionDelayError(s, source.parallelism(), prefetch); + parent = new JoinSubscriptionDelayError<>(s, source.parallelism(), prefetch); } else { - parent = new JoinSubscription(s, source.parallelism(), prefetch); + parent = new JoinSubscription<>(s, source.parallelism(), prefetch); } s.onSubscribe(parent); source.subscribe(parent.subscribers); @@ -81,7 +82,7 @@ abstract static class JoinSubscriptionBase extends AtomicInteger JoinInnerSubscriber[] a = new JoinInnerSubscriber[n]; for (int i = 0; i < n; i++) { - a[i] = new JoinInnerSubscriber(this, prefetch); + a[i] = new JoinInnerSubscriber<>(this, prefetch); } this.subscribers = a; @@ -152,7 +153,7 @@ public void onNext(JoinInnerSubscriber inner, T value) { if (!q.offer(value)) { cancelAll(); - Throwable mbe = new MissingBackpressureException("Queue full?!"); + Throwable mbe = new QueueOverflowException(); if (errors.compareAndSet(null, mbe)) { downstream.onError(mbe); } else { @@ -169,7 +170,7 @@ public void onNext(JoinInnerSubscriber inner, T value) { if (!q.offer(value)) { cancelAll(); - onError(new MissingBackpressureException("Queue full?!")); + onError(new QueueOverflowException()); return; } @@ -298,18 +299,13 @@ void drainLoop() { } } - if (e != 0 && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -337,7 +333,7 @@ void onNext(JoinInnerSubscriber inner, T value) { if (!q.offer(value)) { inner.cancel(); - errors.tryAddThrowableOrReport(new MissingBackpressureException("Queue full?!")); + errors.tryAddThrowableOrReport(new QueueOverflowException()); done.decrementAndGet(); drainLoop(); return; @@ -350,10 +346,9 @@ void onNext(JoinInnerSubscriber inner, T value) { SimplePlainQueue q = inner.getQueue(); if (!q.offer(value)) { - if (inner.cancel()) { - errors.tryAddThrowableOrReport(new MissingBackpressureException("Queue full?!")); - done.decrementAndGet(); - } + inner.cancel(); + errors.tryAddThrowableOrReport(new QueueOverflowException()); + done.decrementAndGet(); } if (getAndIncrement() != 0) { @@ -464,18 +459,13 @@ void drainLoop() { } } - if (e != 0 && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } @@ -550,7 +540,7 @@ public boolean cancel() { SimplePlainQueue getQueue() { SimplePlainQueue q = queue; if (q == null) { - q = new SpscArrayQueue(prefetch); + q = new SpscArrayQueue<>(prefetch); this.queue = q; } return q; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMap.java index b262bfa240..81d70a01d9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,12 +18,13 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Maps each 'rail' of the source ParallelFlowable with a mapper function. * @@ -43,6 +44,8 @@ public ParallelMap(ParallelFlowable source, Function @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -110,7 +113,7 @@ public void onNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null value"); + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancel(); @@ -183,7 +186,7 @@ public void onNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null value"); + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancel(); @@ -202,7 +205,7 @@ public boolean tryOnNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null value"); + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMapTry.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMapTry.java index 8096116ea5..cac64f3711 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMapTry.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelMapTry.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,12 +17,13 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.parallel.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Maps each 'rail' of the source ParallelFlowable with a mapper function * and handle any failure based on a handler function. @@ -48,6 +49,8 @@ public ParallelMapTry(ParallelFlowable source, Function[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -129,14 +132,14 @@ public boolean tryOnNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null value"); + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); ParallelFailureHandling h; try { - h = ObjectHelper.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null item"); + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); } catch (Throwable exc) { Exceptions.throwIfFatal(exc); cancel(); @@ -242,14 +245,14 @@ public boolean tryOnNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper returned a null value"); + v = Objects.requireNonNull(mapper.apply(t), "The mapper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); ParallelFailureHandling h; try { - h = ObjectHelper.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null item"); + h = Objects.requireNonNull(errorHandler.apply(++retries, ex), "The errorHandler returned a null ParallelFailureHandling"); } catch (Throwable exc) { Exceptions.throwIfFatal(exc); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelPeek.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelPeek.java index 4c15e207d4..42f65567ca 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelPeek.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelPeek.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,12 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Execute a Consumer in each 'rail' for the current element passing through. * @@ -53,18 +54,20 @@ public ParallelPeek(ParallelFlowable source, ) { this.source = source; - this.onNext = ObjectHelper.requireNonNull(onNext, "onNext is null"); - this.onAfterNext = ObjectHelper.requireNonNull(onAfterNext, "onAfterNext is null"); - this.onError = ObjectHelper.requireNonNull(onError, "onError is null"); - this.onComplete = ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - this.onAfterTerminated = ObjectHelper.requireNonNull(onAfterTerminated, "onAfterTerminated is null"); - this.onSubscribe = ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - this.onRequest = ObjectHelper.requireNonNull(onRequest, "onRequest is null"); - this.onCancel = ObjectHelper.requireNonNull(onCancel, "onCancel is null"); + this.onNext = Objects.requireNonNull(onNext, "onNext is null"); + this.onAfterNext = Objects.requireNonNull(onAfterNext, "onAfterNext is null"); + this.onError = Objects.requireNonNull(onError, "onError is null"); + this.onComplete = Objects.requireNonNull(onComplete, "onComplete is null"); + this.onAfterTerminated = Objects.requireNonNull(onAfterTerminated, "onAfterTerminated is null"); + this.onSubscribe = Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + this.onRequest = Objects.requireNonNull(onRequest, "onRequest is null"); + this.onCancel = Objects.requireNonNull(onCancel, "onCancel is null"); } @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -74,7 +77,7 @@ public void subscribe(Subscriber[] subscribers) { Subscriber[] parents = new Subscriber[n]; for (int i = 0; i < n; i++) { - parents[i] = new ParallelPeekSubscriber(subscribers[i], this); + parents[i] = new ParallelPeekSubscriber<>(subscribers[i], this); } source.subscribe(parents); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduce.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduce.java index f813149407..b955ca711a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduce.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduce.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,12 +17,13 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscribers.DeferredScalarSubscriber; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Reduce the sequence of values in each 'rail' to a single value. * @@ -45,6 +46,8 @@ public ParallelReduce(ParallelFlowable source, Supplier initialS @Override public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -58,14 +61,14 @@ public void subscribe(Subscriber[] subscribers) { R initialValue; try { - initialValue = ObjectHelper.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); + initialValue = Objects.requireNonNull(initialSupplier.get(), "The initialSupplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); reportError(subscribers, ex); return; } - parents[i] = new ParallelReduceSubscriber(subscribers[i], initialValue, reducer); + parents[i] = new ParallelReduceSubscriber<>(subscribers[i], initialValue, reducer); } source.subscribe(parents); @@ -115,7 +118,7 @@ public void onNext(T t) { R v; try { - v = ObjectHelper.requireNonNull(reducer.apply(accumulator, t), "The reducer returned a null value"); + v = Objects.requireNonNull(reducer.apply(accumulator, t), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduceFull.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduceFull.java index 3a8cfa2290..2e24b9e256 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduceFull.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelReduceFull.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.parallel; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -20,8 +21,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.BiFunction; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -44,7 +45,7 @@ public ParallelReduceFull(ParallelFlowable source, BiFunction s) { - ParallelReduceFullMainSubscriber parent = new ParallelReduceFullMainSubscriber(s, source.parallelism(), reducer); + ParallelReduceFullMainSubscriber parent = new ParallelReduceFullMainSubscriber<>(s, source.parallelism(), reducer); s.onSubscribe(parent); source.subscribe(parent.subscribers); @@ -58,18 +59,18 @@ static final class ParallelReduceFullMainSubscriber extends DeferredScalarSub final BiFunction reducer; - final AtomicReference> current = new AtomicReference>(); + final AtomicReference> current = new AtomicReference<>(); final AtomicInteger remaining = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicThrowable error = new AtomicThrowable(); ParallelReduceFullMainSubscriber(Subscriber subscriber, int n, BiFunction reducer) { super(subscriber); @SuppressWarnings("unchecked") ParallelReduceFullInnerSubscriber[] a = new ParallelReduceFullInnerSubscriber[n]; for (int i = 0; i < n; i++) { - a[i] = new ParallelReduceFullInnerSubscriber(this, reducer); + a[i] = new ParallelReduceFullInnerSubscriber<>(this, reducer); } this.subscribers = a; this.reducer = reducer; @@ -81,7 +82,7 @@ SlotPair addValue(T value) { SlotPair curr = current.get(); if (curr == null) { - curr = new SlotPair(); + curr = new SlotPair<>(); if (!current.compareAndSet(null, curr)) { continue; } @@ -132,7 +133,7 @@ void innerComplete(T value) { if (sp != null) { try { - value = ObjectHelper.requireNonNull(reducer.apply(sp.first, sp.second), "The reducer returned a null value"); + value = Objects.requireNonNull(reducer.apply(sp.first, sp.second), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); innerError(ex); @@ -192,7 +193,7 @@ public void onNext(T t) { } else { try { - v = ObjectHelper.requireNonNull(reducer.apply(v, t), "The reducer returned a null value"); + v = Objects.requireNonNull(reducer.apply(v, t), "The reducer returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); get().cancel(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java index bb6c1fd582..22f822db34 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelRunOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,13 +19,13 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.schedulers.SchedulerMultiWorkerSupport; import io.reactivex.rxjava3.internal.schedulers.SchedulerMultiWorkerSupport.WorkerCallback; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -49,7 +49,9 @@ public ParallelRunOn(ParallelFlowable parent, } @Override - public void subscribe(final Subscriber[] subscribers) { + public void subscribe(Subscriber[] subscribers) { + subscribers = RxJavaPlugins.onSubscribe(this, subscribers); + if (!validate(subscribers)) { return; } @@ -75,12 +77,12 @@ void createSubscriber(int i, Subscriber[] subscribers, Subscriber a = subscribers[i]; - SpscArrayQueue q = new SpscArrayQueue(prefetch); + SpscArrayQueue q = new SpscArrayQueue<>(prefetch); if (a instanceof ConditionalSubscriber) { - parents[i] = new RunOnConditionalSubscriber((ConditionalSubscriber)a, prefetch, q, worker); + parents[i] = new RunOnConditionalSubscriber<>((ConditionalSubscriber)a, prefetch, q, worker); } else { - parents[i] = new RunOnSubscriber(a, prefetch, q, worker); + parents[i] = new RunOnSubscriber<>(a, prefetch, q, worker); } } @@ -146,7 +148,7 @@ public final void onNext(T t) { } if (!queue.offer(t)) { upstream.cancel(); - onError(new MissingBackpressureException("Queue is full?!")); + onError(new QueueOverflowException()); return; } schedule(); @@ -430,19 +432,14 @@ public void run() { } } - if (e != 0L && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0L) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - consumed = c; - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + consumed = c; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java index 0e20b6f2c9..fb20bb65b9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/parallel/ParallelSortedJoin.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -46,7 +46,7 @@ public ParallelSortedJoin(ParallelFlowable> source, Comparator s) { - SortedJoinSubscription parent = new SortedJoinSubscription(s, source.parallelism(), comparator); + SortedJoinSubscription parent = new SortedJoinSubscription<>(s, source.parallelism(), comparator); s.onSubscribe(parent); source.subscribe(parent.subscribers); @@ -74,7 +74,7 @@ static final class SortedJoinSubscription final AtomicInteger remaining = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); @SuppressWarnings("unchecked") SortedJoinSubscription(Subscriber actual, int n, Comparator comparator) { @@ -84,7 +84,7 @@ static final class SortedJoinSubscription SortedJoinInnerSubscriber[] s = new SortedJoinInnerSubscriber[n]; for (int i = 0; i < n; i++) { - s[i] = new SortedJoinInnerSubscriber(this, i); + s[i] = new SortedJoinInnerSubscriber<>(this, i); } this.subscribers = s; this.lists = new List[n]; @@ -215,48 +215,41 @@ void drain() { e++; } - if (e == r) { - if (cancelled) { - Arrays.fill(lists, null); - return; - } + if (cancelled) { + Arrays.fill(lists, null); + return; + } - Throwable ex = error.get(); - if (ex != null) { - cancelAll(); - Arrays.fill(lists, null); - a.onError(ex); - return; - } + Throwable ex = error.get(); + if (ex != null) { + cancelAll(); + Arrays.fill(lists, null); + a.onError(ex); + return; + } - boolean empty = true; + boolean empty = true; - for (int i = 0; i < n; i++) { - if (indexes[i] != lists[i].size()) { - empty = false; - break; - } + for (int i = 0; i < n; i++) { + if (indexes[i] != lists[i].size()) { + empty = false; + break; } + } - if (empty) { - Arrays.fill(lists, null); - a.onComplete(); - return; - } + if (empty) { + Arrays.fill(lists, null); + a.onComplete(); + return; } - if (e != 0 && r != Long.MAX_VALUE) { - requested.addAndGet(-e); + if (e != 0) { + BackpressureHelper.produced(requested, e); } - int w = get(); - if (w == missed) { - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } else { - missed = w; + missed = addAndGet(-missed); + if (missed == 0) { + break; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleAmb.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleAmb.java index 7fd1dba29f..24ff7eabed 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleAmb.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleAmb.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCache.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCache.java index 48220d16b1..7f0848e4c1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCache.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCache.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,12 +39,12 @@ public final class SingleCache extends Single implements SingleObserver public SingleCache(SingleSource source) { this.source = source; this.wip = new AtomicInteger(); - this.observers = new AtomicReference[]>(EMPTY); + this.observers = new AtomicReference<>(EMPTY); } @Override protected void subscribeActual(final SingleObserver observer) { - CacheDisposable d = new CacheDisposable(observer, this); + CacheDisposable d = new CacheDisposable<>(observer, this); observer.onSubscribe(d); if (add(d)) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleContains.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleContains.java index aaa249a6e8..2497fb2047 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleContains.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleContains.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCreate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCreate.java index a2164d2ec5..c0f6c4b1d0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCreate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleCreate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public SingleCreate(SingleOnSubscribe source) { @Override protected void subscribeActual(SingleObserver observer) { - Emitter parent = new Emitter(observer); + Emitter parent = new Emitter<>(observer); observer.onSubscribe(parent); try { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDefer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDefer.java index fff2ca8d58..ddf2b2c347 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDefer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDefer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class SingleDefer extends Single { @@ -32,7 +33,7 @@ protected void subscribeActual(SingleObserver observer) { SingleSource next; try { - next = ObjectHelper.requireNonNull(singleSupplier.get(), "The singleSupplier returned a null SingleSource"); + next = Objects.requireNonNull(singleSupplier.get(), "The singleSupplier returned a null SingleSource"); } catch (Throwable e) { Exceptions.throwIfFatal(e); EmptyDisposable.error(e, observer); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelay.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelay.java index 12da3821fc..1a92d389fa 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelay.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelay.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithCompletable.java index 9915ff1c66..164a502992 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public SingleDelayWithCompletable(SingleSource source, CompletableSource othe @Override protected void subscribeActual(SingleObserver observer) { - other.subscribe(new OtherObserver(observer, source)); + other.subscribe(new OtherObserver<>(observer, source)); } static final class OtherObserver @@ -66,7 +66,7 @@ public void onError(Throwable e) { @Override public void onComplete() { - source.subscribe(new ResumeSingleObserver(this, downstream)); + source.subscribe(new ResumeSingleObserver<>(this, downstream)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java index 78c843db4b..58ed261e71 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public SingleDelayWithObservable(SingleSource source, ObservableSource oth @Override protected void subscribeActual(SingleObserver observer) { - other.subscribe(new OtherSubscriber(observer, source)); + other.subscribe(new OtherSubscriber<>(observer, source)); } static final class OtherSubscriber @@ -56,7 +56,7 @@ static final class OtherSubscriber @Override public void onSubscribe(Disposable d) { - if (DisposableHelper.set(this, d)) { + if (DisposableHelper.setOnce(this, d)) { downstream.onSubscribe(this); } @@ -84,7 +84,7 @@ public void onComplete() { return; } done = true; - source.subscribe(new ResumeSingleObserver(this, downstream)); + source.subscribe(new ResumeSingleObserver<>(this, downstream)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithPublisher.java index 647f55916b..12eeb7fd13 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public SingleDelayWithPublisher(SingleSource source, Publisher other) { @Override protected void subscribeActual(SingleObserver observer) { - other.subscribe(new OtherSubscriber(observer, source)); + other.subscribe(new OtherSubscriber<>(observer, source)); } static final class OtherSubscriber @@ -92,7 +92,7 @@ public void onComplete() { return; } done = true; - source.subscribe(new ResumeSingleObserver(this, downstream)); + source.subscribe(new ResumeSingleObserver<>(this, downstream)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithSingle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithSingle.java index 721d4dc2a5..1f2ffb21dd 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithSingle.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayWithSingle.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public SingleDelayWithSingle(SingleSource source, SingleSource other) { @Override protected void subscribeActual(SingleObserver observer) { - other.subscribe(new OtherObserver(observer, source)); + other.subscribe(new OtherObserver<>(observer, source)); } static final class OtherObserver @@ -61,7 +61,7 @@ public void onSubscribe(Disposable d) { @Override public void onSuccess(U value) { - source.subscribe(new ResumeSingleObserver(this, downstream)); + source.subscribe(new ResumeSingleObserver<>(this, downstream)); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerialize.java index 15f57b631b..e573560e76 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,8 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; /** * Maps the success value of the source to a Notification, then @@ -41,7 +42,7 @@ public SingleDematerialize(Single source, Function @Override protected void subscribeActual(MaybeObserver observer) { - source.subscribe(new DematerializeObserver(observer, selector)); + source.subscribe(new DematerializeObserver<>(observer, selector)); } static final class DematerializeObserver implements SingleObserver, Disposable { @@ -81,7 +82,7 @@ public void onSuccess(T t) { Notification notification; try { - notification = ObjectHelper.requireNonNull(selector.apply(t), "The selector returned a null Notification"); + notification = Objects.requireNonNull(selector.apply(t), "The selector returned a null Notification"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDetach.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDetach.java index 70e5ae029c..2a9acc8a19 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDetach.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDetach.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public SingleDetach(SingleSource source) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new DetachSingleObserver(observer)); + source.subscribe(new DetachSingleObserver<>(observer)); } static final class DetachSingleObserver implements SingleObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccess.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccess.java index 01fa4ce590..51b73f0216 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccess.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccess.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public SingleDoAfterSuccess(SingleSource source, Consumer onAfterS @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new DoAfterObserver(observer, onAfterSuccess)); + source.subscribe(new DoAfterObserver<>(observer, onAfterSuccess)); } static final class DoAfterObserver implements SingleObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminate.java index bcb604e2ca..651c9ee60d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public SingleDoAfterTerminate(SingleSource source, Action onAfterTerminate) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new DoAfterTerminateObserver(observer, onAfterTerminate)); + source.subscribe(new DoAfterTerminateObserver<>(observer, onAfterTerminate)); } static final class DoAfterTerminateObserver implements SingleObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinally.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinally.java index bfa309be03..a64060cc97 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinally.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinally.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public SingleDoFinally(SingleSource source, Action onFinally) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new DoFinallyObserver(observer, onFinally)); + source.subscribe(new DoFinallyObserver<>(observer, onFinally)); } static final class DoFinallyObserver extends AtomicInteger implements SingleObserver, Disposable { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnDispose.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnDispose.java index 31dd0183f4..8c9fd1cb1b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnDispose.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnDispose.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public SingleDoOnDispose(SingleSource source, Action onDispose) { @Override protected void subscribeActual(final SingleObserver observer) { - source.subscribe(new DoOnDisposeObserver(observer, onDispose)); + source.subscribe(new DoOnDisposeObserver<>(observer, onDispose)); } static final class DoOnDisposeObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnError.java index 87061fdafe..dd24ac210c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnEvent.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnEvent.java index 9aa9fccd8a..de53462380 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnEvent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnEvent.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnLifecycle.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnLifecycle.java new file mode 100644 index 0000000000..2d4e15f27c --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnLifecycle.java @@ -0,0 +1,120 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Invokes callbacks upon {@code onSubscribe} from upstream and + * {@code dispose} from downstream. + * + * @param the element type of the flow + * @since 3.0.0 + */ +public final class SingleDoOnLifecycle extends Single { + + final Single source; + + final Consumer onSubscribe; + + final Action onDispose; + + public SingleDoOnLifecycle(Single upstream, Consumer onSubscribe, + Action onDispose) { + this.source = upstream; + this.onSubscribe = onSubscribe; + this.onDispose = onDispose; + } + + @Override + protected void subscribeActual(SingleObserver observer) { + source.subscribe(new SingleLifecycleObserver<>(observer, onSubscribe, onDispose)); + } + + static final class SingleLifecycleObserver implements SingleObserver, Disposable { + + final SingleObserver downstream; + + final Consumer onSubscribe; + + final Action onDispose; + + Disposable upstream; + + SingleLifecycleObserver(SingleObserver downstream, Consumer onSubscribe, Action onDispose) { + this.downstream = downstream; + this.onSubscribe = onSubscribe; + this.onDispose = onDispose; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + // this way, multiple calls to onSubscribe can show up in tests that use doOnSubscribe to validate behavior + try { + onSubscribe.accept(d); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + d.dispose(); + this.upstream = DisposableHelper.DISPOSED; + EmptyDisposable.error(e, downstream); + return; + } + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(@NonNull T t) { + if (upstream != DisposableHelper.DISPOSED) { + upstream = DisposableHelper.DISPOSED; + downstream.onSuccess(t); + } + } + + @Override + public void onError(@NonNull Throwable e) { + if (upstream != DisposableHelper.DISPOSED) { + upstream = DisposableHelper.DISPOSED; + downstream.onError(e); + } else { + RxJavaPlugins.onError(e); + } + } + + @Override + public void dispose() { + try { + onDispose.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + upstream.dispose(); + upstream = DisposableHelper.DISPOSED; + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSubscribe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSubscribe.java index d7c9230fb7..567f95404b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSubscribe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSubscribe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public SingleDoOnSubscribe(SingleSource source, Consumer @Override protected void subscribeActual(final SingleObserver observer) { - source.subscribe(new DoOnSubscribeSingleObserver(observer, onSubscribe)); + source.subscribe(new DoOnSubscribeSingleObserver<>(observer, onSubscribe)); } static final class DoOnSubscribeSingleObserver implements SingleObserver { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess.java index 2efec6acdd..855dc7e388 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminate.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminate.java index 1bb0ca2dec..d6f5dea7fa 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminate.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminate.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java index 8a9f62473f..d11f938a2e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleEquals.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,11 +13,11 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class SingleEquals extends Single { @@ -68,26 +68,19 @@ public void onSuccess(T value) { values[index] = value; if (count.incrementAndGet() == 2) { - downstream.onSuccess(ObjectHelper.equals(values[0], values[1])); + downstream.onSuccess(Objects.equals(values[0], values[1])); } } @Override public void onError(Throwable e) { - for (;;) { - int state = count.get(); - if (state >= 2) { - RxJavaPlugins.onError(e); - return; - } - if (count.compareAndSet(state, 2)) { - set.dispose(); - downstream.onError(e); - return; - } + int state = count.getAndSet(-1); + if (state == 0 || state == 1) { + set.dispose(); + downstream.onError(e); + } else { + RxJavaPlugins.onError(e); } } - } - } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleError.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleError.java index 89b9bc3baa..f016d2c9a9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleError.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleError.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMap.java index 3128c93e5a..ff38538b09 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; public final class SingleFlatMap extends Single { final SingleSource source; @@ -74,7 +74,7 @@ public void onSuccess(T value) { SingleSource o; try { - o = ObjectHelper.requireNonNull(mapper.apply(value), "The single returned by the mapper is null"); + o = Objects.requireNonNull(mapper.apply(value), "The single returned by the mapper is null"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapBiSelector.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapBiSelector.java new file mode 100644 index 0000000000..7f6f58de84 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapBiSelector.java @@ -0,0 +1,156 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.Exceptions; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Maps a source item to another SingleSource then calls a BiFunction with the + * original item and the secondary item to generate the final result. + * + * @param the main value type + * @param the second value type + * @param the result value type + * @since 3.0.0 + */ +public final class SingleFlatMapBiSelector extends Single { + + final SingleSource source; + + final Function> mapper; + + final BiFunction resultSelector; + + public SingleFlatMapBiSelector(SingleSource source, + Function> mapper, + BiFunction resultSelector) { + this.source = source; + this.mapper = mapper; + this.resultSelector = resultSelector; + } + + @Override + protected void subscribeActual(SingleObserver observer) { + source.subscribe(new FlatMapBiMainObserver(observer, mapper, resultSelector)); + } + + static final class FlatMapBiMainObserver + implements SingleObserver, Disposable { + + final Function> mapper; + + final InnerObserver inner; + + FlatMapBiMainObserver(SingleObserver actual, + Function> mapper, + BiFunction resultSelector) { + this.inner = new InnerObserver<>(actual, resultSelector); + this.mapper = mapper; + } + + @Override + public void dispose() { + DisposableHelper.dispose(inner); + } + + @Override + public boolean isDisposed() { + return DisposableHelper.isDisposed(inner.get()); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.setOnce(inner, d)) { + inner.downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(T value) { + SingleSource next; + + try { + next = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + inner.downstream.onError(ex); + return; + } + + if (DisposableHelper.replace(inner, null)) { + inner.value = value; + next.subscribe(inner); + } + } + + @Override + public void onError(Throwable e) { + inner.downstream.onError(e); + } + + static final class InnerObserver + extends AtomicReference + implements SingleObserver { + + private static final long serialVersionUID = -2897979525538174559L; + + final SingleObserver downstream; + + final BiFunction resultSelector; + + T value; + + InnerObserver(SingleObserver actual, + BiFunction resultSelector) { + this.downstream = actual; + this.resultSelector = resultSelector; + } + + @Override + public void onSubscribe(Disposable d) { + DisposableHelper.setOnce(this, d); + } + + @Override + public void onSuccess(U value) { + T t = this.value; + this.value = null; + + R r; + + try { + r = Objects.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + downstream.onSuccess(r); + } + + @Override + public void onError(Throwable e) { + downstream.onError(e); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletable.java index 03edadb83b..a06241c56c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Maps the success value of the source SingleSource into a Completable. @@ -39,7 +39,7 @@ public SingleFlatMapCompletable(SingleSource source, Function parent = new FlatMapCompletableObserver(observer, mapper); + FlatMapCompletableObserver parent = new FlatMapCompletableObserver<>(observer, mapper); observer.onSubscribe(parent); source.subscribe(parent); } @@ -80,7 +80,7 @@ public void onSuccess(T value) { CompletableSource cs; try { - cs = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); + cs = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null CompletableSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java index cc7f2740b9..6d69726588 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.single; import java.util.Iterator; +import java.util.Objects; import java.util.concurrent.atomic.AtomicLong; import org.reactivestreams.Subscriber; @@ -24,7 +25,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.BackpressureHelper; @@ -48,7 +48,7 @@ public SingleFlatMapIterableFlowable(SingleSource source, @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new FlatMapIterableObserver(s, mapper)); + source.subscribe(new FlatMapIterableObserver<>(s, mapper)); } static final class FlatMapIterableObserver @@ -154,7 +154,7 @@ void drain() { long e = 0L; if (r == Long.MAX_VALUE) { - slowPath(a, iterator); + fastPath(a, iterator); return; } @@ -166,7 +166,7 @@ void drain() { R v; try { - v = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + v = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); a.onError(ex); @@ -213,7 +213,7 @@ void drain() { } } - void slowPath(Subscriber a, Iterator iterator) { + void fastPath(Subscriber a, Iterator iterator) { for (;;) { if (cancelled) { return; @@ -273,11 +273,11 @@ public boolean isEmpty() { @Nullable @Override - public R poll() throws Exception { + public R poll() { Iterator iterator = it; if (iterator != null) { - R v = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + R v = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); if (!iterator.hasNext()) { it = null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservable.java index d742a06e1c..c07131da8c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.single; import java.util.Iterator; +import java.util.Objects; import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.*; @@ -21,7 +22,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.BasicIntQueueDisposable; /** @@ -44,7 +44,7 @@ public SingleFlatMapIterableObservable(SingleSource source, @Override protected void subscribeActual(Observer observer) { - source.subscribe(new FlatMapIterableObserver(observer, mapper)); + source.subscribe(new FlatMapIterableObserver<>(observer, mapper)); } static final class FlatMapIterableObserver @@ -183,11 +183,11 @@ public boolean isEmpty() { @Nullable @Override - public R poll() throws Exception { + public R poll() { Iterator iterator = it; if (iterator != null) { - R v = ObjectHelper.requireNonNull(iterator.next(), "The iterator returned a null value"); + R v = Objects.requireNonNull(iterator.next(), "The iterator returned a null value"); if (!iterator.hasNext()) { it = null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybe.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybe.java index 20e60ab476..c58e6aeeae 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybe.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybe.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; public final class SingleFlatMapMaybe extends Maybe { @@ -75,7 +75,7 @@ public void onSuccess(T value) { MaybeSource ms; try { - ms = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); + ms = Objects.requireNonNull(mapper.apply(value), "The mapper returned a null MaybeSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); onError(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapNotification.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapNotification.java new file mode 100644 index 0000000000..bb592a74a9 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapNotification.java @@ -0,0 +1,147 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; + +/** + * Maps a value into a SingleSource and relays its signal. + * + * @param the source value type + * @param the result value type + * @since 3.0.0 + */ +public final class SingleFlatMapNotification extends Single { + + final SingleSource source; + + final Function> onSuccessMapper; + + final Function> onErrorMapper; + + public SingleFlatMapNotification(SingleSource source, + Function> onSuccessMapper, + Function> onErrorMapper) { + this.source = source; + this.onSuccessMapper = onSuccessMapper; + this.onErrorMapper = onErrorMapper; + } + + @Override + protected void subscribeActual(SingleObserver observer) { + source.subscribe(new FlatMapSingleObserver<>(observer, onSuccessMapper, onErrorMapper)); + } + + static final class FlatMapSingleObserver + extends AtomicReference + implements SingleObserver, Disposable { + + private static final long serialVersionUID = 4375739915521278546L; + + final SingleObserver downstream; + + final Function> onSuccessMapper; + + final Function> onErrorMapper; + + Disposable upstream; + + FlatMapSingleObserver(SingleObserver actual, + Function> onSuccessMapper, + Function> onErrorMapper) { + this.downstream = actual; + this.onSuccessMapper = onSuccessMapper; + this.onErrorMapper = onErrorMapper; + } + + @Override + public void dispose() { + DisposableHelper.dispose(this); + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return DisposableHelper.isDisposed(get()); + } + + @Override + public void onSubscribe(Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(T value) { + SingleSource source; + + try { + source = Objects.requireNonNull(onSuccessMapper.apply(value), "The onSuccessMapper returned a null SingleSource"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(ex); + return; + } + + if (!isDisposed()) { + source.subscribe(new InnerObserver()); + } + } + + @Override + public void onError(Throwable e) { + SingleSource source; + + try { + source = Objects.requireNonNull(onErrorMapper.apply(e), "The onErrorMapper returned a null SingleSource"); + } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); + downstream.onError(new CompositeException(e, ex)); + return; + } + + if (!isDisposed()) { + source.subscribe(new InnerObserver()); + } + } + + final class InnerObserver implements SingleObserver { + + @Override + public void onSubscribe(Disposable d) { + DisposableHelper.setOnce(FlatMapSingleObserver.this, d); + } + + @Override + public void onSuccess(R value) { + downstream.onSuccess(value); + } + + @Override + public void onError(Throwable e) { + downstream.onError(e); + } + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java index 0fb91438c8..59a067832f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; @@ -21,14 +22,13 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; /** * A Flowable that emits items based on applying a specified function to the item emitted by the * source Single, where that function returns a Publisher. *

- * + * *

*
Backpressure:
*
The returned {@code Flowable} honors the backpressure of the downstream consumer @@ -56,7 +56,7 @@ public SingleFlatMapPublisher(SingleSource source, @Override protected void subscribeActual(Subscriber downstream) { - source.subscribe(new SingleFlatMapPublisherObserver(downstream, mapper)); + source.subscribe(new SingleFlatMapPublisherObserver<>(downstream, mapper)); } static final class SingleFlatMapPublisherObserver extends AtomicLong @@ -73,7 +73,7 @@ static final class SingleFlatMapPublisherObserver extends AtomicLong Function> mapper) { this.downstream = actual; this.mapper = mapper; - this.parent = new AtomicReference(); + this.parent = new AtomicReference<>(); } @Override @@ -86,13 +86,15 @@ public void onSubscribe(Disposable d) { public void onSuccess(S value) { Publisher f; try { - f = ObjectHelper.requireNonNull(mapper.apply(value), "the mapper returned a null Publisher"); + f = Objects.requireNonNull(mapper.apply(value), "the mapper returned a null Publisher"); } catch (Throwable e) { Exceptions.throwIfFatal(e); downstream.onError(e); return; } - f.subscribe(this); + if (parent.get() != SubscriptionHelper.CANCELLED) { + f.subscribe(this); + } } @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallable.java index 562032f01e..d1a6b73384 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,12 +13,12 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.Callable; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class SingleFromCallable extends Single { @@ -31,7 +31,7 @@ public SingleFromCallable(Callable callable) { @Override protected void subscribeActual(SingleObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (d.isDisposed()) { @@ -40,7 +40,7 @@ protected void subscribeActual(SingleObserver observer) { T value; try { - value = ObjectHelper.requireNonNull(callable.call(), "The callable returned a null value"); + value = Objects.requireNonNull(callable.call(), "The callable returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (!d.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisher.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisher.java index e18394eb1a..860e47f904 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisher.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplier.java index 04d6ca72cf..83e2dc4780 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,9 +17,10 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Supplier; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import java.util.Objects; + /** * Calls a supplier and emits its value or exception to the incoming SingleObserver. * @param the value type returned @@ -35,7 +36,7 @@ public SingleFromSupplier(Supplier supplier) { @Override protected void subscribeActual(SingleObserver observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); if (d.isDisposed()) { @@ -44,7 +45,7 @@ protected void subscribeActual(SingleObserver observer) { T value; try { - value = ObjectHelper.requireNonNull(supplier.get(), "The supplier returned a null value"); + value = Objects.requireNonNull(supplier.get(), "The supplier returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); if (!d.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromUnsafeSource.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromUnsafeSource.java index 328ab873b6..e09e38c833 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromUnsafeSource.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleFromUnsafeSource.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleHide.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleHide.java index 34f5dc4aed..01a4e36d6e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleHide.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleHide.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java index 8c322ada64..5df3dae35d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,12 +14,10 @@ package io.reactivex.rxjava3.internal.operators.single; import java.util.*; -import java.util.concurrent.Callable; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.functions.*; /** @@ -32,22 +30,17 @@ private SingleInternalHelper() { throw new IllegalStateException("No instances!"); } - enum NoSuchElementCallable implements Supplier, Callable { + enum NoSuchElementSupplier implements Supplier { INSTANCE; @Override - public NoSuchElementException call() throws Exception { - return new NoSuchElementException(); - } - - @Override - public NoSuchElementException get() throws Throwable { + public NoSuchElementException get() { return new NoSuchElementException(); } } - public static Supplier emptyThrower() { - return NoSuchElementCallable.INSTANCE; + public static Supplier emptyThrower() { + return NoSuchElementSupplier.INSTANCE; } @SuppressWarnings("rawtypes") @@ -79,7 +72,7 @@ public boolean hasNext() { @Override public Flowable next() { - return new SingleToFlowable(sit.next()); + return new SingleToFlowable<>(sit.next()); } @Override @@ -98,26 +91,11 @@ static final class ToFlowableIterable implements Iterable> { @Override public Iterator> iterator() { - return new ToFlowableIterator(sources.iterator()); + return new ToFlowableIterator<>(sources.iterator()); } } public static Iterable> iterableToFlowable(final Iterable> sources) { - return new ToFlowableIterable(sources); - } - - @SuppressWarnings("rawtypes") - enum ToObservable implements Function { - INSTANCE; - @SuppressWarnings("unchecked") - @Override - public Observable apply(SingleSource v) { - return new SingleToObservable(v); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public static Function, Observable> toObservable() { - return (Function)ToObservable.INSTANCE; + return new ToFlowableIterable<>(sources); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleJust.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleJust.java index 8495f931db..ceb94b93a1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleJust.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleJust.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,7 +14,7 @@ package io.reactivex.rxjava3.internal.operators.single; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; public final class SingleJust extends Single { @@ -26,7 +26,7 @@ public SingleJust(T value) { @Override protected void subscribeActual(SingleObserver observer) { - observer.onSubscribe(Disposables.disposed()); + observer.onSubscribe(Disposable.disposed()); observer.onSuccess(value); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleLift.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleLift.java index af5f969157..5b127e58cb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleLift.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleLift.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class SingleLift extends Single { @@ -34,7 +35,7 @@ protected void subscribeActual(SingleObserver observer) { SingleObserver sr; try { - sr = ObjectHelper.requireNonNull(onLift.apply(observer), "The onLift returned a null SingleObserver"); + sr = Objects.requireNonNull(onLift.apply(observer), "The onLift returned a null SingleObserver"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); EmptyDisposable.error(ex, observer); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMap.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMap.java index d6ee948d5a..3fb20516ce 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMap.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMap.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,8 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; + +import java.util.Objects; public final class SingleMap extends Single { final SingleSource source; @@ -54,7 +55,7 @@ public void onSubscribe(Disposable d) { public void onSuccess(T value) { R v; try { - v = ObjectHelper.requireNonNull(mapper.apply(value), "The mapper function returned a null value."); + v = Objects.requireNonNull(mapper.apply(value), "The mapper function returned a null value."); } catch (Throwable e) { Exceptions.throwIfFatal(e); onError(e); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterialize.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterialize.java index fd0afc2f98..c09d28ffd1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterialize.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterialize.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,6 +34,6 @@ public SingleMaterialize(Single source) { @Override protected void subscribeActual(SingleObserver> observer) { - source.subscribe(new MaterializeSingleObserver(observer)); + source.subscribe(new MaterializeSingleObserver<>(observer)); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleNever.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleNever.java index ea76fac50d..0feefe196d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleNever.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleNever.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOn.java index 035f458e11..e513e16fa2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public SingleObserveOn(SingleSource source, Scheduler scheduler) { @Override protected void subscribeActual(final SingleObserver observer) { - source.subscribe(new ObserveOnSingleObserver(observer, scheduler)); + source.subscribe(new ObserveOnSingleObserver<>(observer, scheduler)); } static final class ObserveOnSingleObserver extends AtomicReference diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorComplete.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorComplete.java new file mode 100644 index 0000000000..bd083cddb1 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorComplete.java @@ -0,0 +1,43 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.Predicate; +import io.reactivex.rxjava3.internal.operators.maybe.MaybeOnErrorComplete; + +/** + * Emits an onComplete if the source emits an onError and the predicate returns true for + * that Throwable. + * + * @param the value type + * @since 3.0.0 + */ +public final class SingleOnErrorComplete extends Maybe { + + final Single source; + + final Predicate predicate; + + public SingleOnErrorComplete(Single source, + Predicate predicate) { + this.source = source; + this.predicate = predicate; + } + + @Override + protected void subscribeActual(MaybeObserver observer) { + source.subscribe(new MaybeOnErrorComplete.OnErrorCompleteMultiObserver(observer, predicate)); + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn.java index 1a606d2750..d1f8b66fd9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleResumeNext.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleResumeNext.java index 71ccfdc035..554a2a254b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleResumeNext.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleResumeNext.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.observers.ResumeSingleObserver; public final class SingleResumeNext extends Single { @@ -36,7 +36,7 @@ public SingleResumeNext(SingleSource source, @Override protected void subscribeActual(final SingleObserver observer) { - source.subscribe(new ResumeMainSingleObserver(observer, nextFunction)); + source.subscribe(new ResumeMainSingleObserver<>(observer, nextFunction)); } static final class ResumeMainSingleObserver extends AtomicReference @@ -70,7 +70,7 @@ public void onError(Throwable e) { SingleSource source; try { - source = ObjectHelper.requireNonNull(nextFunction.apply(e), "The nextFunction returned a null SingleSource."); + source = Objects.requireNonNull(nextFunction.apply(e), "The nextFunction returned a null SingleSource."); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); downstream.onError(new CompositeException(e, ex)); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn.java index 79187148cd..56cf684e9f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public SingleSubscribeOn(SingleSource source, Scheduler scheduler) @Override protected void subscribeActual(final SingleObserver observer) { - final SubscribeOnObserver parent = new SubscribeOnObserver(observer, source); + final SubscribeOnObserver parent = new SubscribeOnObserver<>(observer, source); observer.onSubscribe(parent); Disposable f = scheduler.scheduleDirect(parent); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntil.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntil.java index e69ac1b118..fb5d0989f6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntil.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntil.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -43,7 +43,7 @@ public SingleTakeUntil(SingleSource source, Publisher other) { @Override protected void subscribeActual(SingleObserver observer) { - TakeUntilMainObserver parent = new TakeUntilMainObserver(observer); + TakeUntilMainObserver parent = new TakeUntilMainObserver<>(observer); observer.onSubscribe(parent); other.subscribe(parent.other); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeInterval.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeInterval.java new file mode 100644 index 0000000000..f6d65f7c14 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeInterval.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.disposables.DisposableHelper; +import io.reactivex.rxjava3.schedulers.Timed; + +/** + * Measures the time between subscription and the success item emission + * from the upstream and emits this as a {@link Timed} success value. + * @param the element type of the sequence + * @since 3.0.0 + */ +public final class SingleTimeInterval extends Single> { + + final SingleSource source; + + final TimeUnit unit; + + final Scheduler scheduler; + + final boolean start; + + public SingleTimeInterval(SingleSource source, TimeUnit unit, Scheduler scheduler, boolean start) { + this.source = source; + this.unit = unit; + this.scheduler = scheduler; + this.start = start; + } + + @Override + protected void subscribeActual(@NonNull SingleObserver> observer) { + source.subscribe(new TimeIntervalSingleObserver<>(observer, unit, scheduler, start)); + } + + static final class TimeIntervalSingleObserver implements SingleObserver, Disposable { + + final SingleObserver> downstream; + + final TimeUnit unit; + + final Scheduler scheduler; + + final long startTime; + + Disposable upstream; + + TimeIntervalSingleObserver(SingleObserver> downstream, TimeUnit unit, Scheduler scheduler, boolean start) { + this.downstream = downstream; + this.unit = unit; + this.scheduler = scheduler; + this.startTime = start ? scheduler.now(unit) : 0L; + } + + @Override + public void onSubscribe(@NonNull Disposable d) { + if (DisposableHelper.validate(this.upstream, d)) { + this.upstream = d; + + downstream.onSubscribe(this); + } + } + + @Override + public void onSuccess(@NonNull T t) { + downstream.onSuccess(new Timed<>(t, scheduler.now(unit) - startTime, unit)); + } + + @Override + public void onError(@NonNull Throwable e) { + downstream.onError(e); + } + + @Override + public void dispose() { + upstream.dispose(); + } + + @Override + public boolean isDisposed() { + return upstream.isDisposed(); + } + } +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeout.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeout.java index 1cb417c8db..cc5b923727 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeout.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeout.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -47,7 +47,7 @@ public SingleTimeout(SingleSource source, long timeout, TimeUnit unit, Schedu @Override protected void subscribeActual(final SingleObserver observer) { - TimeoutMainObserver parent = new TimeoutMainObserver(observer, other, timeout, unit); + TimeoutMainObserver parent = new TimeoutMainObserver<>(observer, other, timeout, unit); observer.onSubscribe(parent); DisposableHelper.replace(parent.task, scheduler.scheduleDirect(parent, timeout, unit)); @@ -103,9 +103,9 @@ public void onError(Throwable e) { this.other = other; this.timeout = timeout; this.unit = unit; - this.task = new AtomicReference(); + this.task = new AtomicReference<>(); if (other != null) { - this.fallback = new TimeoutFallbackObserver(actual); + this.fallback = new TimeoutFallbackObserver<>(actual); } else { this.fallback = null; } @@ -113,11 +113,7 @@ public void onError(Throwable e) { @Override public void run() { - Disposable d = get(); - if (d != DisposableHelper.DISPOSED && compareAndSet(d, DisposableHelper.DISPOSED)) { - if (d != null) { - d.dispose(); - } + if (DisposableHelper.dispose(this)) { SingleSource other = this.other; if (other == null) { downstream.onError(new TimeoutException(timeoutMessage(timeout, unit))); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimer.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimer.java index 5188f3a2c9..07748952c8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleTimer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowable.java index 890f6b0569..e692dea5b9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.single; import org.reactivestreams.Subscriber; diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservable.java index 18ecef340f..4586317740 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.single; import io.reactivex.rxjava3.core.*; @@ -44,7 +45,7 @@ public void subscribeActual(final Observer observer) { * @since 2.2 */ public static SingleObserver create(Observer downstream) { - return new SingleToObservableObserver(downstream); + return new SingleToObservableObserver<>(downstream); } static final class SingleToObservableObserver diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOn.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOn.java index 3f5b20b20c..4b18599c84 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOn.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public SingleUnsubscribeOn(SingleSource source, Scheduler scheduler) { @Override protected void subscribeActual(SingleObserver observer) { - source.subscribe(new UnsubscribeOnSingleObserver(observer, scheduler)); + source.subscribe(new UnsubscribeOnSingleObserver<>(observer, scheduler)); } static final class UnsubscribeOnSingleObserver extends AtomicReference diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUsing.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUsing.java index a0cb1d84d9..55ab6c31ff 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUsing.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleUsing.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class SingleUsing extends Single { @@ -56,7 +56,7 @@ protected void subscribeActual(final SingleObserver observer) { SingleSource source; try { - source = ObjectHelper.requireNonNull(singleFunction.apply(resource), "The singleFunction returned a null SingleSource"); + source = Objects.requireNonNull(singleFunction.apply(resource), "The singleFunction returned a null SingleSource"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArray.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArray.java index 9633e94bc7..b53d2f2184 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArray.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArray.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.operators.single; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.core.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; public final class SingleZipArray extends Single { @@ -40,11 +40,11 @@ protected void subscribeActual(SingleObserver observer) { int n = sources.length; if (n == 1) { - sources[0].subscribe(new SingleMap.MapSingleObserver(observer, new SingletonArrayFunc())); + sources[0].subscribe(new SingleMap.MapSingleObserver<>(observer, new SingletonArrayFunc())); return; } - ZipCoordinator parent = new ZipCoordinator(observer, n, zipper); + ZipCoordinator parent = new ZipCoordinator<>(observer, n, zipper); observer.onSubscribe(parent); @@ -74,7 +74,7 @@ static final class ZipCoordinator extends AtomicInteger implements Disposa final ZipSingleObserver[] observers; - final Object[] values; + Object[] values; @SuppressWarnings("unchecked") ZipCoordinator(SingleObserver observer, int n, Function zipper) { @@ -83,7 +83,7 @@ static final class ZipCoordinator extends AtomicInteger implements Disposa this.zipper = zipper; ZipSingleObserver[] o = new ZipSingleObserver[n]; for (int i = 0; i < n; i++) { - o[i] = new ZipSingleObserver(this, i); + o[i] = new ZipSingleObserver<>(this, i); } this.observers = o; this.values = new Object[n]; @@ -100,22 +100,29 @@ public void dispose() { for (ZipSingleObserver d : observers) { d.dispose(); } + + values = null; } } void innerSuccess(T value, int index) { - values[index] = value; + Object[] values = this.values; + if (values != null) { + values[index] = value; + } if (decrementAndGet() == 0) { R v; try { - v = ObjectHelper.requireNonNull(zipper.apply(values), "The zipper returned a null value"); + v = Objects.requireNonNull(zipper.apply(values), "The zipper returned a null value"); } catch (Throwable ex) { Exceptions.throwIfFatal(ex); + this.values = null; downstream.onError(ex); return; } + this.values = null; downstream.onSuccess(v); } } @@ -134,6 +141,7 @@ void disposeExcept(int index) { void innerError(Throwable ex, int index) { if (getAndSet(0) > 0) { disposeExcept(index); + values = null; downstream.onError(ex); } else { RxJavaPlugins.onError(ex); @@ -179,7 +187,7 @@ public void onError(Throwable e) { final class SingletonArrayFunc implements Function { @Override public R apply(T t) throws Throwable { - return ObjectHelper.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); + return Objects.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterable.java b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterable.java index 215f19a58e..935550ab47 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,6 @@ import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.operators.single.SingleZipArray.ZipCoordinator; public final class SingleZipIterable extends Single { @@ -62,11 +61,11 @@ protected void subscribeActual(SingleObserver observer) { } if (n == 1) { - a[0].subscribe(new SingleMap.MapSingleObserver(observer, new SingletonArrayFunc())); + a[0].subscribe(new SingleMap.MapSingleObserver<>(observer, new SingletonArrayFunc())); return; } - ZipCoordinator parent = new ZipCoordinator(observer, n, zipper); + ZipCoordinator parent = new ZipCoordinator<>(observer, n, zipper); observer.onSubscribe(parent); @@ -82,7 +81,7 @@ protected void subscribeActual(SingleObserver observer) { final class SingletonArrayFunc implements Function { @Override public R apply(T t) throws Throwable { - return ObjectHelper.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); + return Objects.requireNonNull(zipper.apply(new Object[] { t }), "The zipper returned a null value"); } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/queue/MpscLinkedQueue.java b/src/main/java/io/reactivex/rxjava3/internal/queue/MpscLinkedQueue.java index 8458e8d876..e8d19c633e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/queue/MpscLinkedQueue.java +++ b/src/main/java/io/reactivex/rxjava3/internal/queue/MpscLinkedQueue.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; +import io.reactivex.rxjava3.operators.SimplePlainQueue; /** * A multi-producer single consumer unbounded queue. @@ -32,9 +32,9 @@ public final class MpscLinkedQueue implements SimplePlainQueue { private final AtomicReference> consumerNode; public MpscLinkedQueue() { - producerNode = new AtomicReference>(); - consumerNode = new AtomicReference>(); - LinkedQueueNode node = new LinkedQueueNode(); + producerNode = new AtomicReference<>(); + consumerNode = new AtomicReference<>(); + LinkedQueueNode node = new LinkedQueueNode<>(); spConsumerNode(node); xchgProducerNode(node); // this ensures correct construction: StoreLoad } @@ -59,7 +59,7 @@ public boolean offer(final T e) { if (null == e) { throw new NullPointerException("Null is not a valid element"); } - final LinkedQueueNode nextNode = new LinkedQueueNode(e); + final LinkedQueueNode nextNode = new LinkedQueueNode<>(e); final LinkedQueueNode prevProducerNode = xchgProducerNode(nextNode); // Should a producer thread get interrupted here the chain WILL be broken until that thread is resumed // and completes the store in prev.next. @@ -91,6 +91,8 @@ public T poll() { // we have to null out the value because we are going to hang on to the node final T nextValue = nextNode.getAndNullValue(); spConsumerNode(nextNode); + // unlink previous consumer to help gc + currConsumerNode.soNext(null); return nextValue; } else if (currConsumerNode != lvProducerNode()) { @@ -101,6 +103,8 @@ else if (currConsumerNode != lvProducerNode()) { // we have to null out the value because we are going to hang on to the node final T nextValue = nextNode.getAndNullValue(); spConsumerNode(nextNode); + // unlink previous consumer to help gc + currConsumerNode.soNext(null); return nextValue; } return null; diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java index 2788bdfec6..556fcd23c7 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTask.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -35,14 +32,17 @@ abstract class AbstractDirectTask protected final Runnable runnable; + protected final boolean interruptOnCancel; + protected Thread runner; - protected static final FutureTask FINISHED = new FutureTask(Functions.EMPTY_RUNNABLE, null); + protected static final FutureTask FINISHED = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); - protected static final FutureTask DISPOSED = new FutureTask(Functions.EMPTY_RUNNABLE, null); + protected static final FutureTask DISPOSED = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); - AbstractDirectTask(Runnable runnable) { + AbstractDirectTask(Runnable runnable, boolean interruptOnCancel) { this.runnable = runnable; + this.interruptOnCancel = interruptOnCancel; } @Override @@ -51,7 +51,7 @@ public final void dispose() { if (f != FINISHED && f != DISPOSED) { if (compareAndSet(f, DISPOSED)) { if (f != null) { - f.cancel(runner != Thread.currentThread()); + cancelFuture(f); } } } @@ -70,7 +70,7 @@ public final void setFuture(Future future) { break; } if (f == DISPOSED) { - future.cancel(runner != Thread.currentThread()); + cancelFuture(future); break; } if (compareAndSet(f, future)) { @@ -79,8 +79,36 @@ public final void setFuture(Future future) { } } + private void cancelFuture(Future future) { + if (runner == Thread.currentThread()) { + future.cancel(false); + } else { + future.cancel(interruptOnCancel); + } + } + @Override public Runnable getWrappedRunnable() { return runnable; } + + @Override + public String toString() { + String status; + Future f = get(); + if (f == FINISHED) { + status = "Finished"; + } else if (f == DISPOSED) { + status = "Disposed"; + } else { + Thread r = runner; + if (r != null) { + status = "Running on " + runner; + } else { + status = "Waiting"; + } + } + + return getClass().getSimpleName() + "[" + status + "]"; + } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java index eb686b17e0..f6845e660e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ComputationScheduler.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.schedulers; import java.util.concurrent.*; @@ -135,7 +133,7 @@ public ComputationScheduler() { */ public ComputationScheduler(ThreadFactory threadFactory) { this.threadFactory = threadFactory; - this.pool = new AtomicReference(NONE); + this.pool = new AtomicReference<>(NONE); start(); } @@ -175,15 +173,9 @@ public void start() { @Override public void shutdown() { - for (;;) { - FixedSchedulerPool curr = pool.get(); - if (curr == NONE) { - return; - } - if (pool.compareAndSet(curr, NONE)) { - curr.shutdown(); - return; - } + FixedSchedulerPool curr = pool.getAndSet(NONE); + if (curr != NONE) { + curr.shutdown(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancel.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancel.java index 231cb5b3f2..72ac8ef525 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancel.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancel.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,6 +15,7 @@ import java.util.concurrent.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.disposables.Disposable; /** @@ -46,13 +47,12 @@ public boolean isDone() { } @Override - public Object get() throws InterruptedException, ExecutionException { + public Object get() { return null; } @Override - public Object get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { + public Object get(long timeout, @NonNull TimeUnit unit) { return null; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java index 8fd7b33720..fa0bcab7f8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ExecutorScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,9 @@ public final class ExecutorScheduler extends Scheduler { @NonNull final Executor executor; - static final Scheduler HELPER = Schedulers.single(); + static final class SingleHolder { + static final Scheduler HELPER = Schedulers.single(); + } public ExecutorScheduler(@NonNull Executor executor, boolean interruptibleWorker, boolean fair) { this.executor = executor; @@ -58,7 +60,7 @@ public Disposable scheduleDirect(@NonNull Runnable run) { Runnable decoratedRun = RxJavaPlugins.onSchedule(run); try { if (executor instanceof ExecutorService) { - ScheduledDirectTask task = new ScheduledDirectTask(decoratedRun); + ScheduledDirectTask task = new ScheduledDirectTask(decoratedRun, interruptibleWorker); Future f = ((ExecutorService)executor).submit(task); task.setFuture(f); return task; @@ -85,7 +87,7 @@ public Disposable scheduleDirect(@NonNull Runnable run, final long delay, final final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); if (executor instanceof ScheduledExecutorService) { try { - ScheduledDirectTask task = new ScheduledDirectTask(decoratedRun); + ScheduledDirectTask task = new ScheduledDirectTask(decoratedRun, interruptibleWorker); Future f = ((ScheduledExecutorService)executor).schedule(task, delay, unit); task.setFuture(f); return task; @@ -97,7 +99,7 @@ public Disposable scheduleDirect(@NonNull Runnable run, final long delay, final final DelayedRunnable dr = new DelayedRunnable(decoratedRun); - Disposable delayed = HELPER.scheduleDirect(new DelayedDispose(dr), delay, unit); + Disposable delayed = SingleHolder.HELPER.scheduleDirect(new DelayedDispose(dr), delay, unit); dr.timed.replace(delayed); @@ -110,7 +112,7 @@ public Disposable schedulePeriodicallyDirect(@NonNull Runnable run, long initial if (executor instanceof ScheduledExecutorService) { Runnable decoratedRun = RxJavaPlugins.onSchedule(run); try { - ScheduledDirectPeriodicTask task = new ScheduledDirectPeriodicTask(decoratedRun); + ScheduledDirectPeriodicTask task = new ScheduledDirectPeriodicTask(decoratedRun, interruptibleWorker); Future f = ((ScheduledExecutorService)executor).scheduleAtFixedRate(task, initialDelay, period, unit); task.setFuture(f); return task; @@ -140,7 +142,7 @@ public static final class ExecutorWorker extends Scheduler.Worker implements Run public ExecutorWorker(Executor executor, boolean interruptibleWorker, boolean fair) { this.executor = executor; - this.queue = new MpscLinkedQueue(); + this.queue = new MpscLinkedQueue<>(); this.interruptibleWorker = interruptibleWorker; this.fair = fair; } @@ -202,7 +204,7 @@ public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); - ScheduledRunnable sr = new ScheduledRunnable(new SequentialDispose(mar, decoratedRun), tasks); + ScheduledRunnable sr = new ScheduledRunnable(new SequentialDispose(mar, decoratedRun), tasks, interruptibleWorker); tasks.add(sr); if (executor instanceof ScheduledExecutorService) { @@ -215,7 +217,7 @@ public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit return EmptyDisposable.INSTANCE; } } else { - final Disposable d = HELPER.scheduleDirect(sr, delay, unit); + final Disposable d = SingleHolder.HELPER.scheduleDirect(sr, delay, unit); sr.setFuture(new DisposeOnCancel(d)); } @@ -257,9 +259,7 @@ void runFair() { } Runnable run = q.poll(); - if (run != null) { - run.run(); - } + run.run(); // never null because of offer + increment happens first if (disposed) { q.clear(); @@ -322,6 +322,10 @@ public void run() { } try { actual.run(); + } catch (Throwable ex) { + // Exceptions.throwIfFatal(ex); nowhere to go + RxJavaPlugins.onError(ex); + throw ex; } finally { lazySet(true); } @@ -388,7 +392,13 @@ public void run() { thread = Thread.currentThread(); if (compareAndSet(READY, RUNNING)) { try { - run.run(); + try { + run.run(); + } catch (Throwable ex) { + // Exceptions.throwIfFatal(ex); nowhere to go + RxJavaPlugins.onError(ex); + throw ex; + } } finally { thread = null; if (compareAndSet(RUNNING, FINISHED)) { @@ -465,11 +475,17 @@ public void run() { Runnable r = get(); if (r != null) { try { - r.run(); - } finally { - lazySet(null); - timed.lazySet(DisposableHelper.DISPOSED); - direct.lazySet(DisposableHelper.DISPOSED); + try { + r.run(); + } finally { + lazySet(null); + timed.lazySet(DisposableHelper.DISPOSED); + direct.lazySet(DisposableHelper.DISPOSED); + } + } catch (Throwable ex) { + // Exceptions.throwIfFatal(ex); nowhere to go + RxJavaPlugins.onError(ex); + throw ex; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinScheduler.java index b1e47267b9..e2430e74cb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public final class ImmediateThinScheduler extends Scheduler { static final Disposable DISPOSED; static { - DISPOSED = Disposables.empty(); + DISPOSED = Disposable.empty(); DISPOSED.dispose(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java index 678c82e794..1fd37f5d93 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTask.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -38,26 +35,28 @@ final class InstantPeriodicTask implements Callable, Disposable { Thread runner; - static final FutureTask CANCELLED = new FutureTask(Functions.EMPTY_RUNNABLE, null); + static final FutureTask CANCELLED = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); InstantPeriodicTask(Runnable task, ExecutorService executor) { super(); this.task = task; - this.first = new AtomicReference>(); - this.rest = new AtomicReference>(); + this.first = new AtomicReference<>(); + this.rest = new AtomicReference<>(); this.executor = executor; } @Override - public Void call() throws Exception { + public Void call() { runner = Thread.currentThread(); try { task.run(); - setRest(executor.submit(this)); runner = null; + setRest(executor.submit(this)); } catch (Throwable ex) { + // Exceptions.throwIfFatal(ex); nowhere to go runner = null; RxJavaPlugins.onError(ex); + throw ex; } return null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java index f4edcf0208..cfe9520685 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/IoScheduler.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -48,6 +45,10 @@ public final class IoScheduler extends Scheduler { /** The name of the system property for setting the thread priority for this Scheduler. */ private static final String KEY_IO_PRIORITY = "rx3.io-priority"; + /** The name of the system property for setting the release behaviour for this Scheduler. */ + private static final String KEY_SCHEDULED_RELEASE = "rx3.io-scheduled-release"; + static boolean USE_SCHEDULED_RELEASE; + static final CachedWorkerPool NONE; static { @@ -63,6 +64,8 @@ public final class IoScheduler extends Scheduler { EVICTOR_THREAD_FACTORY = new RxThreadFactory(EVICTOR_THREAD_NAME_PREFIX, priority); + USE_SCHEDULED_RELEASE = Boolean.getBoolean(KEY_SCHEDULED_RELEASE); + NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY); NONE.shutdown(); } @@ -77,7 +80,7 @@ static final class CachedWorkerPool implements Runnable { CachedWorkerPool(long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) { this.keepAliveTime = unit != null ? unit.toNanos(keepAliveTime) : 0L; - this.expiringWorkerQueue = new ConcurrentLinkedQueue(); + this.expiringWorkerQueue = new ConcurrentLinkedQueue<>(); this.allWorkers = new CompositeDisposable(); this.threadFactory = threadFactory; @@ -93,7 +96,7 @@ static final class CachedWorkerPool implements Runnable { @Override public void run() { - evictExpiredWorkers(); + evictExpiredWorkers(expiringWorkerQueue, allWorkers); } ThreadWorker get() { @@ -120,7 +123,7 @@ void release(ThreadWorker threadWorker) { expiringWorkerQueue.offer(threadWorker); } - void evictExpiredWorkers() { + static void evictExpiredWorkers(ConcurrentLinkedQueue expiringWorkerQueue, CompositeDisposable allWorkers) { if (!expiringWorkerQueue.isEmpty()) { long currentTimestamp = now(); @@ -138,7 +141,7 @@ void evictExpiredWorkers() { } } - long now() { + static long now() { return System.nanoTime(); } @@ -164,7 +167,7 @@ public IoScheduler() { */ public IoScheduler(ThreadFactory threadFactory) { this.threadFactory = threadFactory; - this.pool = new AtomicReference(NONE); + this.pool = new AtomicReference<>(NONE); start(); } @@ -178,15 +181,9 @@ public void start() { @Override public void shutdown() { - for (;;) { - CachedWorkerPool curr = pool.get(); - if (curr == NONE) { - return; - } - if (pool.compareAndSet(curr, NONE)) { - curr.shutdown(); - return; - } + CachedWorkerPool curr = pool.getAndSet(NONE); + if (curr != NONE) { + curr.shutdown(); } } @@ -200,7 +197,7 @@ public int size() { return pool.get().allWorkers.size(); } - static final class EventLoopWorker extends Scheduler.Worker { + static final class EventLoopWorker extends Scheduler.Worker implements Runnable { private final CompositeDisposable tasks; private final CachedWorkerPool pool; private final ThreadWorker threadWorker; @@ -218,11 +215,20 @@ public void dispose() { if (once.compareAndSet(false, true)) { tasks.dispose(); - // releasing the pool should be the last action - pool.release(threadWorker); + if (USE_SCHEDULED_RELEASE) { + threadWorker.scheduleActual(this, 0, TimeUnit.NANOSECONDS, null); + } else { + // releasing the pool should be the last action + pool.release(threadWorker); + } } } + @Override + public void run() { + pool.release(threadWorker); + } + @Override public boolean isDisposed() { return once.get(); @@ -241,7 +247,8 @@ public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull Ti } static final class ThreadWorker extends NewThreadWorker { - private long expirationTime; + + long expirationTime; ThreadWorker(ThreadFactory threadFactory) { super(threadFactory); diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadScheduler.java index 1cd48ce1e0..1c29efddc5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadScheduler.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java index d811c8e782..764241e76e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NewThreadWorker.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ * worker but doesn't perform task-tracking operations. * */ -public class NewThreadWorker extends Scheduler.Worker implements Disposable { +public class NewThreadWorker extends Scheduler.Worker { private final ScheduledExecutorService executor; volatile boolean disposed; @@ -59,7 +59,7 @@ public Disposable schedule(@NonNull final Runnable action, long delayTime, @NonN * @return the ScheduledRunnable instance */ public Disposable scheduleDirect(final Runnable run, long delayTime, TimeUnit unit) { - ScheduledDirectTask task = new ScheduledDirectTask(RxJavaPlugins.onSchedule(run)); + ScheduledDirectTask task = new ScheduledDirectTask(RxJavaPlugins.onSchedule(run), true); try { Future f; if (delayTime <= 0L) { @@ -104,7 +104,7 @@ public Disposable schedulePeriodicallyDirect(Runnable run, long initialDelay, lo return periodicWrapper; } - ScheduledDirectPeriodicTask task = new ScheduledDirectPeriodicTask(decoratedRun); + ScheduledDirectPeriodicTask task = new ScheduledDirectPeriodicTask(decoratedRun, true); try { Future f = executor.scheduleAtFixedRate(task, initialDelay, period, unit); task.setFuture(f); @@ -116,10 +116,8 @@ public Disposable schedulePeriodicallyDirect(Runnable run, long initialDelay, lo } /** - * Wraps the given runnable into a ScheduledRunnable and schedules it + * Wraps and returns the given runnable into a ScheduledRunnable and schedules it * on the underlying ScheduledExecutorService. - *

If the schedule has been rejected, the ScheduledRunnable.wasScheduled will return - * false. * @param run the runnable instance * @param delayTime the time to delay the execution * @param unit the time unit diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NonBlockingThread.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NonBlockingThread.java index f800f1ac4e..ff6dd5c706 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/NonBlockingThread.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/NonBlockingThread.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactory.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactory.java index 7d0646056d..9b3a7968c3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactory.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactory.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,8 @@ package io.reactivex.rxjava3.internal.schedulers; +import io.reactivex.rxjava3.annotations.NonNull; + import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; @@ -47,7 +49,7 @@ public RxThreadFactory(String prefix, int priority, boolean nonBlocking) { } @Override - public Thread newThread(Runnable r) { + public Thread newThread(@NonNull Runnable r) { StringBuilder nameBuilder = new StringBuilder(prefix).append('-').append(incrementAndGet()); // if (CREATE_TRACE) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java index 695760f2ca..1862035dde 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTask.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -27,8 +24,8 @@ public final class ScheduledDirectPeriodicTask extends AbstractDirectTask implem private static final long serialVersionUID = 1811839108042568751L; - public ScheduledDirectPeriodicTask(Runnable runnable) { - super(runnable); + public ScheduledDirectPeriodicTask(Runnable runnable, boolean interruptOnCancel) { + super(runnable, interruptOnCancel); } @Override @@ -38,9 +35,11 @@ public void run() { runnable.run(); runner = null; } catch (Throwable ex) { + // Exceptions.throwIfFatal(ex); nowhere to go + dispose(); runner = null; - lazySet(FINISHED); RxJavaPlugins.onError(ex); + throw ex; } } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java index 89bd9d3d01..6ca8992971 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectTask.java @@ -1,23 +1,22 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; import java.util.concurrent.Callable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + /** * A Callable to be submitted to an ExecutorService that runs a Runnable * action and manages completion/cancellation. @@ -27,18 +26,24 @@ public final class ScheduledDirectTask extends AbstractDirectTask implements Cal private static final long serialVersionUID = 1811839108042568751L; - public ScheduledDirectTask(Runnable runnable) { - super(runnable); + public ScheduledDirectTask(Runnable runnable, boolean interruptOnCancel) { + super(runnable, interruptOnCancel); } @Override - public Void call() throws Exception { + public Void call() { runner = Thread.currentThread(); try { - runnable.run(); - } finally { - lazySet(FINISHED); - runner = null; + try { + runnable.run(); + } finally { + lazySet(FINISHED); + runner = null; + } + } catch (Throwable ex) { + // Exceptions.throwIfFatal(e); nowhere to go + RxJavaPlugins.onError(ex); + throw ex; } return null; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java index 45072a2abd..2a1baa641a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,6 +24,7 @@ public final class ScheduledRunnable extends AtomicReferenceArray private static final long serialVersionUID = -6120223772001106981L; final Runnable actual; + final boolean interruptOnCancel; /** Indicates that the parent tracking this task has been notified about its completion. */ static final Object PARENT_DISPOSED = new Object(); @@ -41,12 +42,26 @@ public final class ScheduledRunnable extends AtomicReferenceArray /** * Creates a ScheduledRunnable by wrapping the given action and setting * up the optional parent. + * The underlying future will be interrupted if the task is disposed asynchronously. * @param actual the runnable to wrap, not-null (not verified) * @param parent the parent tracking container or null if none */ public ScheduledRunnable(Runnable actual, DisposableContainer parent) { + this(actual, parent, true); + } + + /** + * Creates a ScheduledRunnable by wrapping the given action and setting + * up the optional parent. + * @param actual the runnable to wrap, not-null (not verified) + * @param parent the parent tracking container or null if none + * @param interruptOnCancel if true, the underlying future will be interrupted when disposing + * this task from a different thread than it is running on. + */ + public ScheduledRunnable(Runnable actual, DisposableContainer parent, boolean interruptOnCancel) { super(3); this.actual = actual; + this.interruptOnCancel = interruptOnCancel; this.lazySet(0, parent); } @@ -66,9 +81,9 @@ public void run() { } catch (Throwable e) { // Exceptions.throwIfFatal(e); nowhere to go RxJavaPlugins.onError(e); + throw e; } } finally { - lazySet(THREAD_INDEX, null); Object o = get(PARENT_INDEX); if (o != PARENT_DISPOSED && compareAndSet(PARENT_INDEX, o, DONE) && o != null) { ((DisposableContainer)o).delete(this); @@ -80,6 +95,7 @@ public void run() { break; } } + lazySet(THREAD_INDEX, null); } } @@ -94,7 +110,7 @@ public void setFuture(Future f) { return; } if (o == ASYNC_DISPOSED) { - f.cancel(true); + f.cancel(interruptOnCancel); return; } if (compareAndSet(FUTURE_INDEX, o, f)) { @@ -113,7 +129,7 @@ public void dispose() { boolean async = get(THREAD_INDEX) != Thread.currentThread(); if (compareAndSet(FUTURE_INDEX, o, async ? ASYNC_DISPOSED : SYNC_DISPOSED)) { if (o != null) { - ((Future)o).cancel(async); + ((Future)o).cancel(async && interruptOnCancel); } break; } @@ -136,4 +152,26 @@ public boolean isDisposed() { Object o = get(PARENT_INDEX); return o == PARENT_DISPOSED || o == DONE; } + + @Override + public String toString() { + String state; + Object o = get(FUTURE_INDEX); + if (o == DONE) { + state = "Finished"; + } else if (o == SYNC_DISPOSED) { + state = "Disposed(Sync)"; + } else if (o == ASYNC_DISPOSED) { + state = "Disposed(Async)"; + } else { + o = get(THREAD_INDEX); + if (o == null) { + state = "Waiting"; + } else { + state = "Running on " + o; + } + } + + return getClass().getSimpleName() + "[" + state + "]"; + } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupport.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupport.java index d42fa32cfb..b1e186adc9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupport.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupport.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java index cc82349cfc..44a824a168 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactory.java @@ -1,25 +1,21 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; -import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.exceptions.Exceptions; import io.reactivex.rxjava3.functions.Function; /** @@ -33,85 +29,11 @@ private SchedulerPoolFactory() { static final String PURGE_ENABLED_KEY = "rx3.purge-enabled"; - /** - * Indicates the periodic purging of the ScheduledExecutorService is enabled. - */ public static final boolean PURGE_ENABLED; - static final String PURGE_PERIOD_SECONDS_KEY = "rx3.purge-period-seconds"; - - /** - * Indicates the purge period of the ScheduledExecutorServices created by create(). - */ - public static final int PURGE_PERIOD_SECONDS; - - static final AtomicReference PURGE_THREAD = - new AtomicReference(); - - // Upcast to the Map interface here to avoid 8.x compatibility issues. - // See http://stackoverflow.com/a/32955708/61158 - static final Map POOLS = - new ConcurrentHashMap(); - - /** - * Starts the purge thread if not already started. - */ - public static void start() { - tryStart(PURGE_ENABLED); - } - - static void tryStart(boolean purgeEnabled) { - if (purgeEnabled) { - for (;;) { - ScheduledExecutorService curr = PURGE_THREAD.get(); - if (curr != null) { - return; - } - ScheduledExecutorService next = Executors.newScheduledThreadPool(1, new RxThreadFactory("RxSchedulerPurge")); - if (PURGE_THREAD.compareAndSet(curr, next)) { - - next.scheduleAtFixedRate(new ScheduledTask(), PURGE_PERIOD_SECONDS, PURGE_PERIOD_SECONDS, TimeUnit.SECONDS); - - return; - } else { - next.shutdownNow(); - } - } - } - } - - /** - * Stops the purge thread. - */ - public static void shutdown() { - ScheduledExecutorService exec = PURGE_THREAD.getAndSet(null); - if (exec != null) { - exec.shutdownNow(); - } - POOLS.clear(); - } - static { SystemPropertyAccessor propertyAccessor = new SystemPropertyAccessor(); PURGE_ENABLED = getBooleanProperty(true, PURGE_ENABLED_KEY, true, true, propertyAccessor); - PURGE_PERIOD_SECONDS = getIntProperty(PURGE_ENABLED, PURGE_PERIOD_SECONDS_KEY, 1, 1, propertyAccessor); - - start(); - } - - static int getIntProperty(boolean enabled, String key, int defaultNotFound, int defaultNotEnabled, Function propertyAccessor) { - if (enabled) { - try { - String value = propertyAccessor.apply(key); - if (value == null) { - return defaultNotFound; - } - return Integer.parseInt(value); - } catch (Throwable ex) { - return defaultNotFound; - } - } - return defaultNotEnabled; } static boolean getBooleanProperty(boolean enabled, String key, boolean defaultNotFound, boolean defaultNotEnabled, Function propertyAccessor) { @@ -123,6 +45,7 @@ static boolean getBooleanProperty(boolean enabled, String key, boolean defaultNo } return "true".equals(value); } catch (Throwable ex) { + Exceptions.throwIfFatal(ex); return defaultNotFound; } } @@ -131,7 +54,7 @@ static boolean getBooleanProperty(boolean enabled, String key, boolean defaultNo static final class SystemPropertyAccessor implements Function { @Override - public String apply(String t) throws Throwable { + public String apply(String t) { return System.getProperty(t); } } @@ -142,28 +65,8 @@ public String apply(String t) throws Throwable { * @return the ScheduledExecutorService */ public static ScheduledExecutorService create(ThreadFactory factory) { - final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory); - tryPutIntoPool(PURGE_ENABLED, exec); + final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1, factory); + exec.setRemoveOnCancelPolicy(PURGE_ENABLED); return exec; } - - static void tryPutIntoPool(boolean purgeEnabled, ScheduledExecutorService exec) { - if (purgeEnabled && exec instanceof ScheduledThreadPoolExecutor) { - ScheduledThreadPoolExecutor e = (ScheduledThreadPoolExecutor) exec; - POOLS.put(e, exec); - } - } - - static final class ScheduledTask implements Runnable { - @Override - public void run() { - for (ScheduledThreadPoolExecutor e : new ArrayList(POOLS.keySet())) { - if (e.isShutdown()) { - POOLS.remove(e); - } else { - e.purge(); - } - } - } - } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java index 5a15e61223..814c971f1c 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhen.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.schedulers; import java.util.concurrent.TimeUnit; @@ -51,13 +49,15 @@ *

* Limit the amount concurrency two at a time without creating a new fix size * thread pool: - * + * *

+ * {@code
  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
  *  // use merge max concurrent to limit the number of concurrent
  *  // callbacks two at a time
  *  return Completable.merge(Observable.merge(workers), 2);
  * });
+ * }
  * 
*

* This is a slightly different way to limit the concurrency but it has some @@ -69,21 +69,24 @@ * {@link Flowable#zip(org.reactivestreams.Publisher, org.reactivestreams.Publisher, io.reactivex.rxjava3.functions.BiFunction)} where * subscribing to the first {@link Observable} could deadlock the subscription * to the second. - * + * *

+ * {@code
  * Scheduler limitScheduler = Schedulers.computation().when(workers -> {
  *  // use merge max concurrent to limit the number of concurrent
  *  // Observables two at a time
  *  return Completable.merge(Observable.merge(workers, 2));
  * });
+ * }
  * 
- * - * Slowing down the rate to no more than than 1 a second. This suffers from the + * + * Slowing down the rate to no more than 1 a second. This suffers from the * same problem as the one above I could find an {@link Observable} operator * that limits the rate without dropping the values (aka leaky bucket * algorithm). - * + * *
+ * {@code
  * Scheduler slowScheduler = Schedulers.computation().when(workers -> {
  *  // use concatenate to make each worker happen one at a time.
  *  return Completable.concat(workers.map(actions -> {
@@ -91,6 +94,7 @@
  *      return Completable.merge(actions.delaySubscription(1, TimeUnit.SECONDS));
  *  }));
  * });
+ * }
  * 
*

History 2.0.1 - experimental * @since 2.1 @@ -145,7 +149,7 @@ public Worker createWorker() { static final Disposable SUBSCRIBED = new SubscribedDisposable(); - static final Disposable DISPOSED = Disposables.disposed(); + static final Disposable DISPOSED = Disposable.disposed(); @SuppressWarnings("serial") abstract static class ScheduledAction extends AtomicReference implements Disposable { @@ -187,21 +191,7 @@ public boolean isDisposed() { @Override public void dispose() { - Disposable oldState; - // no matter what the current state is the new state is going to be - Disposable newState = DISPOSED; - do { - oldState = get(); - if (oldState == DISPOSED) { - // the action has already been unsubscribed - return; - } - } while (!compareAndSet(oldState, newState)); - - if (oldState != SUBSCRIBED) { - // the action was scheduled. stop it. - oldState.dispose(); - } + getAndSet(DISPOSED).dispose(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java index a1dba3036b..98d3b04626 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/SingleScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.schedulers; import java.util.concurrent.*; @@ -28,7 +29,7 @@ public final class SingleScheduler extends Scheduler { final ThreadFactory threadFactory; - final AtomicReference executor = new AtomicReference(); + final AtomicReference executor = new AtomicReference<>(); /** The name of the system property for setting the thread priority for this Scheduler. */ private static final String KEY_SINGLE_PRIORITY = "rx3.single-priority"; @@ -90,12 +91,9 @@ public void start() { @Override public void shutdown() { - ScheduledExecutorService current = executor.get(); + ScheduledExecutorService current = executor.getAndSet(SHUTDOWN); if (current != SHUTDOWN) { - current = executor.getAndSet(SHUTDOWN); - if (current != SHUTDOWN) { - current.shutdownNow(); - } + current.shutdownNow(); } } @@ -108,7 +106,7 @@ public Worker createWorker() { @NonNull @Override public Disposable scheduleDirect(@NonNull Runnable run, long delay, TimeUnit unit) { - ScheduledDirectTask task = new ScheduledDirectTask(RxJavaPlugins.onSchedule(run)); + ScheduledDirectTask task = new ScheduledDirectTask(RxJavaPlugins.onSchedule(run), true); try { Future f; if (delay <= 0L) { @@ -148,7 +146,7 @@ public Disposable schedulePeriodicallyDirect(@NonNull Runnable run, long initial return periodicWrapper; } - ScheduledDirectPeriodicTask task = new ScheduledDirectPeriodicTask(decoratedRun); + ScheduledDirectPeriodicTask task = new ScheduledDirectPeriodicTask(decoratedRun, true); try { Future f = executor.get().scheduleAtFixedRate(task, initialDelay, period, unit); task.setFuture(f); diff --git a/src/main/java/io/reactivex/rxjava3/internal/schedulers/TrampolineScheduler.java b/src/main/java/io/reactivex/rxjava3/internal/schedulers/TrampolineScheduler.java index cc61d797e9..04496482b0 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/schedulers/TrampolineScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/internal/schedulers/TrampolineScheduler.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -23,7 +20,6 @@ import io.reactivex.rxjava3.core.Scheduler; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -66,8 +62,8 @@ public Disposable scheduleDirect(@NonNull Runnable run, long delay, TimeUnit uni return EmptyDisposable.INSTANCE; } - static final class TrampolineWorker extends Scheduler.Worker implements Disposable { - final PriorityBlockingQueue queue = new PriorityBlockingQueue(); + static final class TrampolineWorker extends Scheduler.Worker { + final PriorityBlockingQueue queue = new PriorityBlockingQueue<>(); private final AtomicInteger wip = new AtomicInteger(); @@ -121,7 +117,7 @@ Disposable enqueue(Runnable action, long execTime) { return EmptyDisposable.INSTANCE; } else { // queue wasn't empty, a parent is already processing so we just add to the end of the queue - return Disposables.fromRunnable(new AppendToQueueTask(timedRunnable)); + return Disposable.fromRunnable(new AppendToQueueTask(timedRunnable)); } } @@ -165,9 +161,9 @@ static final class TimedRunnable implements Comparable { @Override public int compareTo(TimedRunnable that) { - int result = ObjectHelper.compare(execTime, that.execTime); + int result = Long.compare(execTime, that.execTime); if (result == 0) { - return ObjectHelper.compare(count, that.count); + return Integer.compare(count, that.count); } return result; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber.java index ae1e72c3f8..645a3171f2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,8 +16,9 @@ import org.reactivestreams.Subscription; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber.java index b0944cacec..162a9dbe55 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,8 +17,8 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingBaseSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingBaseSubscriber.java index cf93064c6a..68fa0df3eb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingBaseSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingBaseSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.subscribers; import java.util.concurrent.CountDownLatch; diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingFirstSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingFirstSubscriber.java index 228023b3d9..1226eca845 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingFirstSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingFirstSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingLastSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingLastSubscriber.java index 1889193770..05be9d79fa 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingLastSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingLastSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriber.java index 9ddba51e3c..4617833fc4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriber.java index 7ea697a3ab..a55f4ae4fa 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -136,4 +136,4 @@ public void cancel() { public boolean hasCustomOnError() { return onError != Functions.ON_ERROR_MISSING; } -} \ No newline at end of file +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriber.java index 5fca99286a..11123962f2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java new file mode 100644 index 0000000000..3b9d2d99b3 --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/DisposableAutoReleaseSubscriber.java @@ -0,0 +1,151 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.subscribers; + +import java.util.concurrent.atomic.AtomicReference; + +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.core.FlowableSubscriber; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Wraps lambda callbacks and when the upstream terminates or this subscriber gets disposed, + * removes itself from a {@link io.reactivex.rxjava3.disposables.CompositeDisposable}. + *

History: 0.18.0 @ RxJavaExtensions + * @param the element type consumed + * @since 3.1.0 + */ +public final class DisposableAutoReleaseSubscriber +extends AtomicReference +implements FlowableSubscriber, Disposable, LambdaConsumerIntrospection { + + private static final long serialVersionUID = 8924480688481408726L; + + final AtomicReference composite; + + final Consumer onNext; + + final Consumer onError; + + final Action onComplete; + + public DisposableAutoReleaseSubscriber( + DisposableContainer composite, + Consumer onNext, + Consumer onError, + Action onComplete + ) { + this.onNext = onNext; + this.onError = onError; + this.onComplete = onComplete; + this.composite = new AtomicReference<>(composite); + } + + @Override + public void onNext(T t) { + if (get() != SubscriptionHelper.CANCELLED) { + try { + onNext.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + get().cancel(); + onError(e); + } + } + } + + @Override + public void onError(Throwable t) { + if (get() != SubscriptionHelper.CANCELLED) { + lazySet(SubscriptionHelper.CANCELLED); + try { + onError.accept(t); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(new CompositeException(t, e)); + } + } else { + RxJavaPlugins.onError(t); + } + removeSelf(); + } + + @Override + public void onComplete() { + if (get() != SubscriptionHelper.CANCELLED) { + lazySet(SubscriptionHelper.CANCELLED); + try { + onComplete.run(); + } catch (Throwable e) { + Exceptions.throwIfFatal(e); + RxJavaPlugins.onError(e); + } + } + removeSelf(); + } + + @Override + public void dispose() { + SubscriptionHelper.cancel(this); + removeSelf(); + } + + void removeSelf() { + DisposableContainer c = composite.getAndSet(null); + if (c != null) { + c.delete(this); + } + } + + @Override + public boolean isDisposed() { + return SubscriptionHelper.CANCELLED == get(); + } + + @Override + public void onSubscribe(Subscription s) { + if (SubscriptionHelper.setOnce(this, s)) { + s.request(Long.MAX_VALUE); + } + } + + @Override + public boolean hasCustomOnError() { + return onError != Functions.ON_ERROR_MISSING; + } + +} diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/ForEachWhileSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/ForEachWhileSubscriber.java index 34d00eef9d..cb5ff01a13 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/ForEachWhileSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/ForEachWhileSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java index 1e09f5b414..145195e2cc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,6 +21,7 @@ import org.reactivestreams.Subscription; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BlockingHelper; @@ -42,7 +43,7 @@ public final class FutureSubscriber extends CountDownLatch public FutureSubscriber() { super(1); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); } @Override @@ -91,7 +92,7 @@ public T get() throws InterruptedException, ExecutionException { } @Override - public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + public T get(long timeout, @NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (getCount() != 0) { BlockingHelper.verifyNonBlocking(); if (!await(timeout, unit)) { @@ -127,18 +128,16 @@ public void onNext(T t) { @Override public void onError(Throwable t) { - for (;;) { + if (error == null) { Subscription a = upstream.get(); - if (a == this || a == SubscriptionHelper.CANCELLED) { - RxJavaPlugins.onError(t); - return; - } - error = t; - if (upstream.compareAndSet(a, this)) { + if (a != this && a != SubscriptionHelper.CANCELLED + && upstream.compareAndSet(a, this)) { + error = t; countDown(); return; } } + RxJavaPlugins.onError(t); } @Override @@ -147,15 +146,12 @@ public void onComplete() { onError(new NoSuchElementException("The source is empty")); return; } - for (;;) { - Subscription a = upstream.get(); - if (a == this || a == SubscriptionHelper.CANCELLED) { - return; - } - if (upstream.compareAndSet(a, this)) { - countDown(); - return; - } + Subscription a = upstream.get(); + if (a == this || a == SubscriptionHelper.CANCELLED) { + return; + } + if (upstream.compareAndSet(a, this)) { + countDown(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java index c53138e3d7..f946ec7deb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,9 +18,10 @@ import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.FlowableSubscriber; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.QueueDrainHelper; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; /** * Subscriber that can fuse with the upstream and calls a support interface @@ -115,18 +116,6 @@ public void request(long n) { } } - public void requestOne() { - if (fusionMode != QueueSubscription.SYNC) { - long p = produced + 1; - if (p == limit) { - produced = 0L; - get().request(p); - } else { - produced = p; - } - } - } - @Override public void cancel() { SubscriptionHelper.cancel(this); diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberSupport.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberSupport.java index e7deb5c411..3844c5b981 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberSupport.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberSupport.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriber.java index daa76fdfbf..f137237e97 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriber.java index c66505a071..8fb7f55295 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,9 +20,9 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.SimplePlainQueue; /** * Abstract base class for subscribers that hold another subscriber, a queue @@ -84,7 +84,7 @@ protected final void fastPathEmitMax(U value, boolean delayError, Disposable dis } } else { dispose.dispose(); - s.onError(new MissingBackpressureException("Could not emit buffer due to lack of requests")); + s.onError(MissingBackpressureException.createDefault()); return; } } else { @@ -118,7 +118,7 @@ protected final void fastPathOrderedEmitMax(U value, boolean delayError, Disposa } else { cancelled = true; dispose.dispose(); - s.onError(new MissingBackpressureException("Could not emit buffer due to lack of requests")); + s.onError(MissingBackpressureException.createDefault()); return; } } else { diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriber.java index ab9f9ba799..ceec9f6b90 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriber.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriber.java index 32668ed4d9..265acd60a5 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -57,7 +57,7 @@ public StrictSubscriber(Subscriber downstream) { this.downstream = downstream; this.error = new AtomicThrowable(); this.requested = new AtomicLong(); - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.once = new AtomicBoolean(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapper.java b/src/main/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapper.java index 55d052c705..df85e95324 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public final class SubscriberResourceWrapper extends AtomicReference downstream; - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); public SubscriberResourceWrapper(Subscriber downstream) { this.downstream = downstream; diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscription.java index 628891a7e6..ce4b2b5ac8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscription.java index 370c45ffbe..d33d05c8d1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,8 +35,8 @@ public final class AsyncSubscription extends AtomicLong implements Subscription, final AtomicReference resource; public AsyncSubscription() { - resource = new AtomicReference(); - actual = new AtomicReference(); + resource = new AtomicReference<>(); + actual = new AtomicReference<>(); } public AsyncSubscription(Disposable resource) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription.java index 862fb51b4b..46b420354b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,14 +15,15 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.operators.QueueSubscription; /** * Base class extending AtomicInteger (wip or request accounting) and QueueSubscription (fusion). * * @param the value type */ -public abstract class BasicIntQueueSubscription extends AtomicInteger implements QueueSubscription { +public abstract class BasicIntQueueSubscription<@NonNull T> extends AtomicInteger implements QueueSubscription { private static final long serialVersionUID = -6671519529404341862L; diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicQueueSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicQueueSubscription.java index 54d33f26cb..684bdf4e80 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicQueueSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BasicQueueSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import java.util.concurrent.atomic.AtomicLong; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; +import io.reactivex.rxjava3.operators.QueueSubscription; /** * Base class extending AtomicLong (wip or request accounting) and QueueSubscription (fusion). diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BooleanSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BooleanSubscription.java index 117b09b60c..2d7f01ef05 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BooleanSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/BooleanSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.subscriptions; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java index b50b4e13f9..544fac8b58 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,7 @@ import org.reactivestreams.Subscriber; -import io.reactivex.rxjava3.annotations.Nullable; +import io.reactivex.rxjava3.annotations.*; /** * A subscription that signals a single value eventually. @@ -33,7 +33,7 @@ * Where exclusively set means any other bits are 0 when that bit is set. * @param the value type */ -public class DeferredScalarSubscription extends BasicIntQueueSubscription { +public class DeferredScalarSubscription<@NonNull T> extends BasicIntQueueSubscription { private static final long serialVersionUID = -2151279923272604993L; @@ -115,7 +115,7 @@ public final void complete(T v) { lazySet(FUSED_READY); Subscriber a = downstream; - a.onNext(v); + a.onNext(null); if (get() != CANCELLED) { a.onComplete(); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/EmptySubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/EmptySubscription.java index 2da5f529a1..6b2c033d5e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/EmptySubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/EmptySubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; +import io.reactivex.rxjava3.operators.QueueSubscription; /** * An empty subscription that does nothing other than validates the request amount. diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscription.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscription.java index e854c11f92..ef1e35e753 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; +import io.reactivex.rxjava3.operators.QueueSubscription; /** * A Subscription that holds a constant value and emits it only when requested. diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiter.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiter.java index c3fb7955fa..7d964224eb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiter.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -12,24 +12,12 @@ */ package io.reactivex.rxjava3.internal.subscriptions; -/** - * 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. - */ +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.Subscription; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; /** @@ -63,7 +51,7 @@ public class SubscriptionArbiter extends AtomicInteger implements Subscription { public SubscriptionArbiter(boolean cancelOnReplace) { this.cancelOnReplace = cancelOnReplace; - missedSubscription = new AtomicReference(); + missedSubscription = new AtomicReference<>(); missedRequested = new AtomicLong(); missedProduced = new AtomicLong(); } @@ -78,7 +66,7 @@ public final void setSubscription(Subscription s) { return; } - ObjectHelper.requireNonNull(s, "s is null"); + Objects.requireNonNull(s, "s is null"); if (get() == 0 && compareAndSet(0, 1)) { Subscription a = actual; diff --git a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper.java b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper.java index 7080b3472c..922ac9c3c9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,12 +13,12 @@ package io.reactivex.rxjava3.internal.subscriptions; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.Subscription; import io.reactivex.rxjava3.exceptions.ProtocolViolationException; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -129,7 +129,7 @@ public static boolean set(AtomicReference field, Subscription s) { * @return true if the operation succeeded, false if the target field was not null. */ public static boolean setOnce(AtomicReference field, Subscription s) { - ObjectHelper.requireNonNull(s, "s is null"); + Objects.requireNonNull(s, "s is null"); if (!field.compareAndSet(null, s)) { s.cancel(); if (field.get() != CANCELLED) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList.java b/src/main/java/io/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList.java index a2a78e7b7e..3c17b88009 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/ArrayListSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/util/ArrayListSupplier.java index 1f82daf237..6379cfcab8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/ArrayListSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/ArrayListSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,10 +32,10 @@ public static Function> asFunction() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } @Override public List apply(Object o) { - return new ArrayList(); + return new ArrayList<>(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/AtomicThrowable.java b/src/main/java/io/reactivex/rxjava3/internal/util/AtomicThrowable.java index 222008e216..3ef92cb77a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/AtomicThrowable.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/AtomicThrowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/BackpressureHelper.java b/src/main/java/io/reactivex/rxjava3/internal/util/BackpressureHelper.java index 9237c11a18..73b15382c8 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/BackpressureHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/BackpressureHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,10 +10,12 @@ * 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 io.reactivex.rxjava3.internal.util; import java.util.concurrent.atomic.AtomicLong; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -26,10 +28,10 @@ private BackpressureHelper() { } /** - * Adds two long values and caps the sum at Long.MAX_VALUE. + * Adds two long values and caps the sum at {@link Long#MAX_VALUE}. * @param a the first value * @param b the second value - * @return the sum capped at Long.MAX_VALUE + * @return the sum capped at {@link Long#MAX_VALUE} */ public static long addCap(long a, long b) { long u = a + b; @@ -40,10 +42,10 @@ public static long addCap(long a, long b) { } /** - * Multiplies two long values and caps the product at Long.MAX_VALUE. + * Multiplies two long values and caps the product at {@link Long#MAX_VALUE}. * @param a the first value * @param b the second value - * @return the product capped at Long.MAX_VALUE + * @return the product capped at {@link Long#MAX_VALUE} */ public static long multiplyCap(long a, long b) { long u = a * b; @@ -56,13 +58,13 @@ public static long multiplyCap(long a, long b) { } /** - * Atomically adds the positive value n to the requested value in the AtomicLong and - * caps the result at Long.MAX_VALUE and returns the previous value. - * @param requested the AtomicLong holding the current requested value + * Atomically adds the positive value n to the requested value in the {@link AtomicLong} and + * caps the result at {@link Long#MAX_VALUE} and returns the previous value. + * @param requested the {@code AtomicLong} holding the current requested value * @param n the value to add, must be positive (not verified) * @return the original value before the add */ - public static long add(AtomicLong requested, long n) { + public static long add(@NonNull AtomicLong requested, long n) { for (;;) { long r = requested.get(); if (r == Long.MAX_VALUE) { @@ -76,14 +78,14 @@ public static long add(AtomicLong requested, long n) { } /** - * Atomically adds the positive value n to the requested value in the AtomicLong and - * caps the result at Long.MAX_VALUE and returns the previous value and - * considers Long.MIN_VALUE as a cancel indication (no addition then). - * @param requested the AtomicLong holding the current requested value + * Atomically adds the positive value n to the requested value in the {@link AtomicLong} and + * caps the result at {@link Long#MAX_VALUE} and returns the previous value and + * considers {@link Long#MIN_VALUE} as a cancel indication (no addition then). + * @param requested the {@code AtomicLong} holding the current requested value * @param n the value to add, must be positive (not verified) * @return the original value before the add */ - public static long addCancel(AtomicLong requested, long n) { + public static long addCancel(@NonNull AtomicLong requested, long n) { for (;;) { long r = requested.get(); if (r == Long.MIN_VALUE) { @@ -100,12 +102,12 @@ public static long addCancel(AtomicLong requested, long n) { } /** - * Atomically subtract the given number (positive, not validated) from the target field unless it contains Long.MAX_VALUE. + * Atomically subtract the given number (positive, not validated) from the target field unless it contains {@link Long#MAX_VALUE}. * @param requested the target field holding the current requested amount * @param n the produced element count, positive (not validated) * @return the new amount */ - public static long produced(AtomicLong requested, long n) { + public static long produced(@NonNull AtomicLong requested, long n) { for (;;) { long current = requested.get(); if (current == Long.MAX_VALUE) { @@ -124,12 +126,12 @@ public static long produced(AtomicLong requested, long n) { /** * Atomically subtract the given number (positive, not validated) from the target field if - * it doesn't contain Long.MIN_VALUE (indicating some cancelled state) or Long.MAX_VALUE (unbounded mode). + * it doesn't contain {@link Long#MIN_VALUE} (indicating some cancelled state) or {@link Long#MAX_VALUE} (unbounded mode). * @param requested the target field holding the current requested amount * @param n the produced element count, positive (not validated) * @return the new amount */ - public static long producedCancel(AtomicLong requested, long n) { + public static long producedCancel(@NonNull AtomicLong requested, long n) { for (;;) { long current = requested.get(); if (current == Long.MIN_VALUE) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/BlockingHelper.java b/src/main/java/io/reactivex/rxjava3/internal/util/BlockingHelper.java index 57d9f863b3..84acadee6f 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/BlockingHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/BlockingHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/BlockingIgnoringReceiver.java b/src/main/java/io/reactivex/rxjava3/internal/util/BlockingIgnoringReceiver.java index f0d90e4708..8d4c1fb76e 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/BlockingIgnoringReceiver.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/BlockingIgnoringReceiver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/ConnectConsumer.java b/src/main/java/io/reactivex/rxjava3/internal/util/ConnectConsumer.java index 4d4f160a8a..5d5246719d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/ConnectConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/ConnectConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ public final class ConnectConsumer implements Consumer { public Disposable disposable; @Override - public void accept(Disposable t) throws Exception { + public void accept(Disposable t) { this.disposable = t; } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/EmptyComponent.java b/src/main/java/io/reactivex/rxjava3/internal/util/EmptyComponent.java index 1ffc9c51d4..47390f57cb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/EmptyComponent.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/EmptyComponent.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/EndConsumerHelper.java b/src/main/java/io/reactivex/rxjava3/internal/util/EndConsumerHelper.java index 1e051a636c..0e72353800 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/EndConsumerHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/EndConsumerHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.internal.util; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import org.reactivestreams.Subscription; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.ProtocolViolationException; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -50,7 +50,7 @@ private EndConsumerHelper() { * @return true if successful, false if the upstream was non null */ public static boolean validate(Disposable upstream, Disposable next, Class observer) { - ObjectHelper.requireNonNull(next, "next is null"); + Objects.requireNonNull(next, "next is null"); if (upstream != null) { next.dispose(); if (upstream != DisposableHelper.DISPOSED) { @@ -72,7 +72,7 @@ public static boolean validate(Disposable upstream, Disposable next, Class ob * @return true if successful, false if the content of the AtomicReference was non null */ public static boolean setOnce(AtomicReference upstream, Disposable next, Class observer) { - ObjectHelper.requireNonNull(next, "next is null"); + Objects.requireNonNull(next, "next is null"); if (!upstream.compareAndSet(null, next)) { next.dispose(); if (upstream.get() != DisposableHelper.DISPOSED) { @@ -95,7 +95,7 @@ public static boolean setOnce(AtomicReference upstream, Disposable n * @return true if successful, false if the upstream was non null */ public static boolean validate(Subscription upstream, Subscription next, Class subscriber) { - ObjectHelper.requireNonNull(next, "next is null"); + Objects.requireNonNull(next, "next is null"); if (upstream != null) { next.cancel(); if (upstream != SubscriptionHelper.CANCELLED) { @@ -117,7 +117,7 @@ public static boolean validate(Subscription upstream, Subscription next, Class upstream, Subscription next, Class subscriber) { - ObjectHelper.requireNonNull(next, "next is null"); + Objects.requireNonNull(next, "next is null"); if (!upstream.compareAndSet(null, next)) { next.cancel(); if (upstream.get() != SubscriptionHelper.CANCELLED) { diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/ErrorMode.java b/src/main/java/io/reactivex/rxjava3/internal/util/ErrorMode.java index e3ff3d5fdb..02389f0b7a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/ErrorMode.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/ErrorMode.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/ExceptionHelper.java b/src/main/java/io/reactivex/rxjava3/internal/util/ExceptionHelper.java index 606368d041..1ddbb8aca1 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/ExceptionHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/ExceptionHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -52,7 +52,7 @@ public static RuntimeException wrapOrThrow(Throwable error) { */ public static final Throwable TERMINATED = new Termination(); - public static boolean addThrowable(AtomicReference field, Throwable exception) { + public static boolean addThrowable(AtomicReference field, Throwable exception) { for (;;) { Throwable current = field.get(); @@ -73,7 +73,7 @@ public static boolean addThrowable(AtomicReference field, Throwab } } - public static Throwable terminate(AtomicReference field) { + public static Throwable terminate(AtomicReference field) { Throwable current = field.get(); if (current != TERMINATED) { current = field.getAndSet(TERMINATED); @@ -87,8 +87,8 @@ public static Throwable terminate(AtomicReference field) { * @return the list of Throwables flattened in a depth-first manner */ public static List flatten(Throwable t) { - List list = new ArrayList(); - ArrayDeque deque = new ArrayDeque(); + List list = new ArrayList<>(); + ArrayDeque deque = new ArrayDeque<>(); deque.offer(t); while (!deque.isEmpty()) { @@ -165,7 +165,7 @@ public static NullPointerException createNullPointerException(String prefix) { } /** - * Similar to ObjectHelper.requireNonNull but composes the error message via + * Similar to Objects.requireNonNull but composes the error message via * {@link #nullWarning(String)}. * @param the value type * @param value the value to check diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java b/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java index 54f787f55a..e1bf5596e2 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/HalfSerializer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.util; import java.util.concurrent.atomic.AtomicInteger; @@ -37,15 +38,18 @@ private HalfSerializer() { * @param value the value to emit * @param wip the serialization work-in-progress counter/indicator * @param errors the holder of Throwables + * @return true if the operation succeeded, false if there sequence completed */ - public static void onNext(Subscriber subscriber, T value, + public static boolean onNext(Subscriber subscriber, T value, AtomicInteger wip, AtomicThrowable errors) { if (wip.get() == 0 && wip.compareAndSet(0, 1)) { subscriber.onNext(value); - if (wip.decrementAndGet() != 0) { - errors.tryTerminateConsumer(subscriber); + if (wip.decrementAndGet() == 0) { + return true; } + errors.tryTerminateConsumer(subscriber); } + return false; } /** diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/HashMapSupplier.java b/src/main/java/io/reactivex/rxjava3/internal/util/HashMapSupplier.java index 22d9900d19..15c2884266 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/HashMapSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/HashMapSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,6 +26,6 @@ public static Supplier> asSupplier() { } @Override public Map get() { - return new HashMap(); + return new HashMap<>(); } } diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/LinkedArrayList.java b/src/main/java/io/reactivex/rxjava3/internal/util/LinkedArrayList.java index f4b1d4aae2..92ff9d30a6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/LinkedArrayList.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/LinkedArrayList.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.util; import java.util.*; @@ -92,7 +93,7 @@ public int size() { public String toString() { final int cap = capacityHint; final int s = size; - final List list = new ArrayList(s + 1); + final List list = new ArrayList<>(s + 1); Object[] h = head(); int j = 0; diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/ListAddBiConsumer.java b/src/main/java/io/reactivex/rxjava3/internal/util/ListAddBiConsumer.java index 9f484f1700..fb643fcff3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/ListAddBiConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/ListAddBiConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public static BiFunction, T, List> instance() { @SuppressWarnings("unchecked") @Override - public List apply(List t1, Object t2) throws Exception { + public List apply(List t1, Object t2) { t1.add(t2); return t1; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/MergerBiFunction.java b/src/main/java/io/reactivex/rxjava3/internal/util/MergerBiFunction.java index 9efe097a7c..a736309019 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/MergerBiFunction.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/MergerBiFunction.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,12 +30,12 @@ public MergerBiFunction(Comparator comparator) { } @Override - public List apply(List a, List b) throws Exception { + public List apply(List a, List b) { int n = a.size() + b.size(); if (n == 0) { - return new ArrayList(); + return new ArrayList<>(); } - List both = new ArrayList(n); + List both = new ArrayList<>(n); Iterator at = a.iterator(); Iterator bt = b.iterator(); diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/NotificationLite.java b/src/main/java/io/reactivex/rxjava3/internal/util/NotificationLite.java index dbc54c788a..c27486d9cf 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/NotificationLite.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/NotificationLite.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,15 +10,16 @@ * 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 io.reactivex.rxjava3.internal.util; import java.io.Serializable; +import java.util.Objects; import org.reactivestreams.*; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Lightweight notification handling utility class. @@ -52,7 +53,7 @@ public int hashCode() { public boolean equals(Object obj) { if (obj instanceof ErrorNotification) { ErrorNotification n = (ErrorNotification) obj; - return ObjectHelper.equals(e, n.e); + return Objects.equals(e, n.e); } return false; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/ObservableQueueDrain.java b/src/main/java/io/reactivex/rxjava3/internal/util/ObservableQueueDrain.java index c0187e9cc2..6d69b375d4 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/ObservableQueueDrain.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/ObservableQueueDrain.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/OpenHashSet.java b/src/main/java/io/reactivex/rxjava3/internal/util/OpenHashSet.java index c5bf3bb676..9967ca831d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/OpenHashSet.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/OpenHashSet.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/Pow2.java b/src/main/java/io/reactivex/rxjava3/internal/util/Pow2.java index dd6df01d9e..fd30ee04fb 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/Pow2.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/Pow2.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,6 +15,7 @@ * Original License: https://github.com/JCTools/JCTools/blob/master/LICENSE * Original location: https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/util/Pow2.java */ + package io.reactivex.rxjava3.internal.util; public final class Pow2 { diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrain.java b/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrain.java index 4a84e5e6ed..481cbe0012 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrain.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrain.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java b/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java index 41906b9cf7..fa0c500892 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/QueueDrainHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.util; import java.util.Queue; @@ -21,8 +22,7 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.BooleanSupplier; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.*; +import io.reactivex.rxjava3.operators.*; /** * Utility class to help with the queue-drain serialization idiom. @@ -78,7 +78,7 @@ public static void drainMaxLoop(SimplePlainQueue q, Subscriber boolean checkTerminated(boolean d, boolean empty, */ public static SimpleQueue createQueue(int capacityHint) { if (capacityHint < 0) { - return new SpscLinkedArrayQueue(-capacityHint); + return new SpscLinkedArrayQueue<>(-capacityHint); } - return new SpscArrayQueue(capacityHint); + return new SpscArrayQueue<>(capacityHint); } /** - * Requests Long.MAX_VALUE if prefetch is negative or the exact + * Requests {@link Long#MAX_VALUE} if prefetch is negative or the exact * amount if prefetch is positive. * @param s the Subscription to request from * @param prefetch the prefetch value @@ -290,6 +290,7 @@ static boolean isCancelled(BooleanSupplier cancelled) { /** * Drains the queue based on the outstanding requests in post-completed mode (only!). * + * @param the value type * @param n the current request amount * @param actual the target Subscriber to send events to * @param queue the queue to drain if in the post-complete state @@ -383,7 +384,7 @@ static boolean postCompleteDrain(long n, * in completed mode, requests no-longer reach the upstream but help in draining the queue. *

* The algorithm utilizes the most significant bit (bit 63) of a long value (AtomicLong) since - * request amount only goes up to Long.MAX_VALUE (bits 0-62) and negative values aren't + * request amount only goes up to {@link Long#MAX_VALUE} (bits 0-62) and negative values aren't * allowed. * * @param the value type emitted diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/SorterFunction.java b/src/main/java/io/reactivex/rxjava3/internal/util/SorterFunction.java index 5a9929f604..b74f1bccf6 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/SorterFunction.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/SorterFunction.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public SorterFunction(Comparator comparator) { } @Override - public List apply(List t) throws Exception { + public List apply(List t) { Collections.sort(t, comparator); return t; } diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/SuppressAnimalSniffer.java b/src/main/java/io/reactivex/rxjava3/internal/util/SuppressAnimalSniffer.java index e57d883036..b55fb0673b 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/SuppressAnimalSniffer.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/SuppressAnimalSniffer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayList.java b/src/main/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayList.java index 301dc634fa..798a05a6dc 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayList.java +++ b/src/main/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayList.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,8 @@ package io.reactivex.rxjava3.internal.util; +import io.reactivex.rxjava3.annotations.NonNull; + import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -29,11 +31,11 @@ public final class VolatileSizeArrayList extends AtomicInteger implements Lis final ArrayList list; public VolatileSizeArrayList() { - list = new ArrayList(); + list = new ArrayList<>(); } public VolatileSizeArrayList(int initialCapacity) { - list = new ArrayList(initialCapacity); + list = new ArrayList<>(initialCapacity); } @Override @@ -62,7 +64,7 @@ public Object[] toArray() { } @Override - public E[] toArray(E[] a) { + public E[] toArray(@NonNull E[] a) { return list.toArray(a); } @@ -81,33 +83,33 @@ public boolean remove(Object o) { } @Override - public boolean containsAll(Collection c) { + public boolean containsAll(@NonNull Collection c) { return list.containsAll(c); } @Override - public boolean addAll(Collection c) { + public boolean addAll(@NonNull Collection c) { boolean b = list.addAll(c); lazySet(list.size()); return b; } @Override - public boolean addAll(int index, Collection c) { + public boolean addAll(int index, @NonNull Collection c) { boolean b = list.addAll(index, c); lazySet(list.size()); return b; } @Override - public boolean removeAll(Collection c) { + public boolean removeAll(@NonNull Collection c) { boolean b = list.removeAll(c); lazySet(list.size()); return b; } @Override - public boolean retainAll(Collection c) { + public boolean retainAll(@NonNull Collection c) { boolean b = list.retainAll(c); lazySet(list.size()); return b; diff --git a/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java b/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java index 135f183ceb..231d0357fe 100644 --- a/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java +++ b/src/main/java/io/reactivex/rxjava3/observables/ConnectableObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.observables; +import java.util.Objects; import java.util.concurrent.TimeUnit; import io.reactivex.rxjava3.annotations.*; @@ -31,22 +32,21 @@ * can wait for all intended {@link Observer}s to {@link Observable#subscribe} to the {@code Observable} * before the {@code Observable} begins emitting items. *

- * + * *

* When the upstream terminates, the {@code ConnectableObservable} remains in this terminated state and, - * depending on the actual underlying implementation, relays cached events to late {@link Observer}s. + * depending on the actual underlying implementation, relays cached events to late {@code Observer}s. * In order to reuse and restart this {@code ConnectableObservable}, the {@link #reset()} method has to be called. - * When called, this {@code ConnectableObservable} will appear as fresh, unconnected source to new {@link Observer}s. - * Disposing the connection will reset the {@code ConnectableFlowable} to its fresh state and there is no need to call - * {@code reset()} in this case. + * When called, this {@code ConnectableObservable} will appear as fresh, unconnected source to new {@code Observer}s. + * Disposing the connection will reset the {@code ConnectableObservable} to its fresh state and there is no need to call + * {@link #reset()} in this case. *

* Note that although {@link #connect()} and {@link #reset()} are safe to call from multiple threads, it is recommended * a dedicated thread or business logic manages the connection or resetting of a {@code ConnectableObservable} so that * there is no unwanted signal loss due to early {@code connect()} or {@code reset()} calls while {@code Observer}s are * still being subscribed to to this {@code ConnectableObservable} to receive signals from the get go. * - * @see RxJava Wiki: - * Connectable Observable Operators + * @see RxJava Wiki: Connectable Observable Operators * @param * the type of items emitted by the {@code ConnectableObservable} */ @@ -55,21 +55,32 @@ public abstract class ConnectableObservable extends Observable { /** * Instructs the {@code ConnectableObservable} to begin emitting the items from its underlying * {@link Observable} to its {@link Observer}s. + *

+ *
Scheduler:
+ *
The behavior is determined by the implementor of this abstract class.
+ *
* * @param connection * the action that receives the connection subscription before the subscription to source happens * allowing the caller to synchronously disconnect a synchronous source + * @throws NullPointerException if {@code connection} is {@code null} * @see ReactiveX documentation: Connect */ + @SchedulerSupport(SchedulerSupport.NONE) public abstract void connect(@NonNull Consumer connection); /** - * Resets this ConnectableObservable into its fresh state if it has terminated + * Resets this {@code ConnectableObservable} into its fresh state if it has terminated * or has been disposed. *

* Calling this method on a fresh or active {@code ConnectableObservable} has no effect. + *

+ *
Scheduler:
+ *
The behavior is determined by the implementor of this abstract class.
+ *
* @since 3.0.0 */ + @SchedulerSupport(SchedulerSupport.NONE) public abstract void reset(); /** @@ -77,10 +88,16 @@ public abstract class ConnectableObservable extends Observable { * {@link Observable} to its {@link Observer}s. *

* To disconnect from a synchronous source, use the {@link #connect(Consumer)} method. + *

+ *
Scheduler:
+ *
The behavior is determined by the implementor of this abstract class.
+ *
* - * @return the subscription representing the connection + * @return the {@link Disposable} representing the connection * @see ReactiveX documentation: Connect */ + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) public final Disposable connect() { ConnectConsumer cc = new ConnectConsumer(); connect(cc); @@ -88,13 +105,13 @@ public final Disposable connect() { } /** - * Returns an {@code Observable} that stays connected to this {@code ConnectableObservable} as long as there + * Returns an {@link Observable} that stays connected to this {@code ConnectableObservable} as long as there * is at least one subscription to this {@code ConnectableObservable}. *
*
Scheduler:
*
This {@code refCount} overload does not operate on any particular {@link Scheduler}.
*
- * @return an {@link Observable} + * @return a new {@code Observable} instance * @see ReactiveX documentation: RefCount * @see #refCount(int) * @see #refCount(long, TimeUnit) @@ -104,195 +121,227 @@ public final Disposable connect() { @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) public Observable refCount() { - return RxJavaPlugins.onAssembly(new ObservableRefCount(this)); + return RxJavaPlugins.onAssembly(new ObservableRefCount<>(this)); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed - * observers reaches the specified count and disconnect if all subscribers have unsubscribed. + * observers reaches the specified count and disconnect if all {@link Observer}s have unsubscribed. *
*
Scheduler:
*
This {@code refCount} overload does not operate on any particular {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param subscriberCount the number of subscribers required to connect to the upstream - * @return the new Observable instance + * @param observerCount the number of {@code Observer}s required to connect to the upstream + * @return the new {@link Observable} instance + * @throws IllegalArgumentException if {@code observerCount} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) - public final Observable refCount(int subscriberCount) { - return refCount(subscriberCount, 0, TimeUnit.NANOSECONDS, Schedulers.trampoline()); + @NonNull + public final Observable refCount(int observerCount) { + return refCount(observerCount, 0, TimeUnit.NANOSECONDS, Schedulers.trampoline()); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches 1 and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the {@code computation} {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout - * @return the new Observable instance + * @return the new {@link Observable} instance + * @throws NullPointerException if {@code unit} is {@code null} * @see #refCount(long, TimeUnit, Scheduler) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable refCount(long timeout, TimeUnit unit) { + @NonNull + public final Observable refCount(long timeout, @NonNull TimeUnit unit) { return refCount(1, timeout, unit, Schedulers.computation()); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches 1 and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the specified {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Observable instance + * @return the new {@link Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable refCount(long timeout, TimeUnit unit, Scheduler scheduler) { + @NonNull + public final Observable refCount(long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { return refCount(1, timeout, unit, scheduler); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches the specified count and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the {@code computation} {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param subscriberCount the number of subscribers required to connect to the upstream - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param observerCount the number of {@code Observer}s required to connect to the upstream + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout - * @return the new Observable instance + * @return the new {@link Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code observerCount} is non-positive * @see #refCount(int, long, TimeUnit, Scheduler) * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.COMPUTATION) - public final Observable refCount(int subscriberCount, long timeout, TimeUnit unit) { - return refCount(subscriberCount, timeout, unit, Schedulers.computation()); + @NonNull + public final Observable refCount(int observerCount, long timeout, @NonNull TimeUnit unit) { + return refCount(observerCount, timeout, unit, Schedulers.computation()); } /** * Connects to the upstream {@code ConnectableObservable} if the number of subscribed * observers reaches the specified count and disconnect after the specified - * timeout if all subscribers have unsubscribed. + * timeout if all {@link Observer}s have unsubscribed. *

*
Scheduler:
*
This {@code refCount} overload operates on the specified {@link Scheduler}.
*
*

History: 2.1.14 - experimental - * @param subscriberCount the number of subscribers required to connect to the upstream - * @param timeout the time to wait before disconnecting after all subscribers unsubscribed + * @param observerCount the number of {@code Observer}s required to connect to the upstream + * @param timeout the time to wait before disconnecting after all {@code Observer}s unsubscribed * @param unit the time unit of the timeout * @param scheduler the target scheduler to wait on before disconnecting - * @return the new Observable instance + * @return the new {@link Observable} instance + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code observerCount} is non-positive * @since 2.2 */ @CheckReturnValue @SchedulerSupport(SchedulerSupport.CUSTOM) - public final Observable refCount(int subscriberCount, long timeout, TimeUnit unit, Scheduler scheduler) { - ObjectHelper.verifyPositive(subscriberCount, "subscriberCount"); - ObjectHelper.requireNonNull(unit, "unit is null"); - ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - return RxJavaPlugins.onAssembly(new ObservableRefCount(this, subscriberCount, timeout, unit, scheduler)); + @NonNull + public final Observable refCount(int observerCount, long timeout, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + ObjectHelper.verifyPositive(observerCount, "observerCount"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return RxJavaPlugins.onAssembly(new ObservableRefCount<>(this, observerCount, timeout, unit, scheduler)); } /** - * Returns an Observable that automatically connects (at most once) to this ConnectableObservable - * when the first Observer subscribes. + * Returns an {@link Observable} that automatically connects (at most once) to this {@code ConnectableObservable} + * when the first {@link Observer} subscribes. *

* *

* The connection happens after the first subscription and happens at most once - * during the lifetime of the returned Observable. If this ConnectableObservable - * terminates, the connection is never renewed, no matter how Observers come + * during the lifetime of the returned {@code Observable}. If this {@code ConnectableObservable} + * terminates, the connection is never renewed, no matter how {@code Observer}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Observer}s have disposed their {@code Disposable}s. + * connection when all {@code Observer}s have disposed their {@link Disposable}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload * to gain access to the {@code Disposable} representing the only connection. + *

+ *
Scheduler:
+ *
{@code autoConnect} overload does not operate on any particular {@link Scheduler}.
+ *
* - * @return an Observable that automatically connects to this ConnectableObservable - * when the first Observer subscribes + * @return a new {@code Observable} instance that automatically connects to this {@code ConnectableObservable} + * when the first {@code Observer} subscribes */ @NonNull + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) public Observable autoConnect() { return autoConnect(1); } /** - * Returns an Observable that automatically connects (at most once) to this ConnectableObservable - * when the specified number of Observers subscribe to it. + * Returns an {@link Observable} that automatically connects (at most once) to this {@code ConnectableObservable} + * when the specified number of {@link Observer}s subscribe to it. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Observable. If this ConnectableObservable - * terminates, the connection is never renewed, no matter how Observers come + * during the lifetime of the returned {@code Observable}. If this {@code ConnectableObservable} + * terminates, the connection is never renewed, no matter how {@code Observer}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active - * connection when all {@code Observer}s have disposed their {@code Disposable}s. + * connection when all {@code Observer}s have disposed their {@link Disposable}s. *

* This overload does not allow disconnecting the connection established via * {@link #connect(Consumer)}. Use the {@link #autoConnect(int, Consumer)} overload * to gain access to the {@code Disposable} representing the only connection. + *

+ *
Scheduler:
+ *
{@code autoConnect} overload does not operate on any particular {@link Scheduler}.
+ *
* - * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableObservable. A non-positive value indicates + * @param numberOfObservers the number of subscribers to await before calling connect + * on the {@code ConnectableObservable}. A non-positive value indicates * an immediate connection. - * @return an Observable that automatically connects to this ConnectableObservable - * when the specified number of Subscribers subscribe to it + * @return a new {@code Observable} instance that automatically connects to this {@code ConnectableObservable} + * when the specified number of {@code Observer}s subscribe to it */ @NonNull - public Observable autoConnect(int numberOfSubscribers) { - return autoConnect(numberOfSubscribers, Functions.emptyConsumer()); + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + public Observable autoConnect(int numberOfObservers) { + return autoConnect(numberOfObservers, Functions.emptyConsumer()); } /** - * Returns an Observable that automatically connects (at most once) to this ConnectableObservable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection. + * Returns an {@link Observable} that automatically connects (at most once) to this {@code ConnectableObservable} + * when the specified number of {@link Observer}s subscribe to it and calls the + * specified callback with the {@link Disposable} associated with the established connection. *

* *

* The connection happens after the given number of subscriptions and happens at most once - * during the lifetime of the returned Observable. If this ConnectableObservable - * terminates, the connection is never renewed, no matter how Observers come + * during the lifetime of the returned {@code Observable}. If this {@code ConnectableObservable} + * terminates, the connection is never renewed, no matter how {@code Observer}s come * and go. Use {@link #refCount()} to renew a connection or dispose an active * connection when all {@code Observer}s have disposed their {@code Disposable}s. + *

+ *
Scheduler:
+ *
{@code autoConnect} overload does not operate on any particular {@link Scheduler}.
+ *
* - * @param numberOfSubscribers the number of subscribers to await before calling connect - * on the ConnectableObservable. A non-positive value indicates + * @param numberOfObservers the number of subscribers to await before calling connect + * on the {@code ConnectableObservable}. A non-positive value indicates * an immediate connection. - * @param connection the callback Consumer that will receive the Subscription representing the + * @param connection the callback {@link Consumer} that will receive the {@code Disposable} representing the * established connection - * @return an Observable that automatically connects to this ConnectableObservable - * when the specified number of Subscribers subscribe to it and calls the - * specified callback with the Subscription associated with the established connection + * @return a new {@code Observable} instance that automatically connects to this {@code ConnectableObservable} + * when the specified number of {@code Observer}s subscribe to it and calls the + * specified callback with the {@code Disposable} associated with the established connection + * @throws NullPointerException if {@code connection} is {@code null} */ @NonNull - public Observable autoConnect(int numberOfSubscribers, @NonNull Consumer connection) { - if (numberOfSubscribers <= 0) { + @CheckReturnValue + @SchedulerSupport(SchedulerSupport.NONE) + public Observable autoConnect(int numberOfObservers, @NonNull Consumer connection) { + Objects.requireNonNull(connection, "connection is null"); + if (numberOfObservers <= 0) { this.connect(connection); return RxJavaPlugins.onAssembly(this); } - return RxJavaPlugins.onAssembly(new ObservableAutoConnect(this, numberOfSubscribers, connection)); + return RxJavaPlugins.onAssembly(new ObservableAutoConnect<>(this, numberOfObservers, connection)); } } diff --git a/src/main/java/io/reactivex/rxjava3/observables/GroupedObservable.java b/src/main/java/io/reactivex/rxjava3/observables/GroupedObservable.java index 489ee1d716..baa35ce48d 100644 --- a/src/main/java/io/reactivex/rxjava3/observables/GroupedObservable.java +++ b/src/main/java/io/reactivex/rxjava3/observables/GroupedObservable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.observables; import io.reactivex.rxjava3.annotations.Nullable; diff --git a/src/main/java/io/reactivex/rxjava3/observables/package-info.java b/src/main/java/io/reactivex/rxjava3/observables/package-info.java index 7fb2fc1454..93c6ad0c40 100644 --- a/src/main/java/io/reactivex/rxjava3/observables/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/observables/package-info.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ /** diff --git a/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java b/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java index ee2dcf1853..6b3a72fadb 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java +++ b/src/main/java/io/reactivex/rxjava3/observers/BaseTestConsumer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,15 +16,17 @@ import java.util.*; import java.util.concurrent.*; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.exceptions.CompositeException; import io.reactivex.rxjava3.functions.Predicate; -import io.reactivex.rxjava3.internal.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.util.*; /** - * Base class with shared infrastructure to support TestSubscriber and TestObserver. + * Base class with shared infrastructure to support + * {@link io.reactivex.rxjava3.subscribers.TestSubscriber TestSubscriber} and {@link TestObserver}. * @param the value type consumed - * @param the subclass of this BaseTestConsumer + * @param the subclass of this {@code BaseTestConsumer} */ public abstract class BaseTestConsumer> { /** The latch that indicates an onError or onComplete has been called. */ @@ -47,26 +49,29 @@ public abstract class BaseTestConsumer> { protected CharSequence tag; /** - * Indicates that one of the awaitX method has timed out. + * Indicates that one of the {@code awaitX} method has timed out. * @since 2.0.7 */ protected boolean timeout; + /** + * Constructs a {@code BaseTestConsumer} with {@code CountDownLatch} set to 1. + */ public BaseTestConsumer() { - this.values = new VolatileSizeArrayList(); - this.errors = new VolatileSizeArrayList(); + this.values = new VolatileSizeArrayList<>(); + this.errors = new VolatileSizeArrayList<>(); this.done = new CountDownLatch(1); } /** - * Returns a shared list of received onNext values. + * Returns a shared list of received {@code onNext} values or the single {@code onSuccess} value. *

* Note that accessing the items via certain methods of the {@link List} * interface while the upstream is still actively emitting * more items may result in a {@code ConcurrentModificationException}. *

* The {@link List#size()} method will return the number of items - * already received by this TestObserver/TestSubscriber in a thread-safe + * already received by this {@code TestObserver}/{@code TestSubscriber} in a thread-safe * manner that can be read via {@link List#get(int)}) method * (index range of 0 to {@code List.size() - 1}). *

@@ -76,6 +81,7 @@ public BaseTestConsumer() { * {@code ConcurrentModificationException}. * @return a list of received onNext values */ + @NonNull public final List values() { return values; } @@ -89,7 +95,8 @@ public final List values() { * @param message the message to use * @return AssertionError the prepared AssertionError instance */ - protected final AssertionError fail(String message) { + @NonNull + protected final AssertionError fail(@NonNull String message) { StringBuilder b = new StringBuilder(64 + message.length()); b.append(message); @@ -131,11 +138,12 @@ protected final AssertionError fail(String message) { } /** - * Awaits until this TestObserver/TestSubscriber receives an onError or onComplete events. + * Awaits until this {@code TestObserver}/{@code TestSubscriber} receives an {@code onError} or {@code onComplete} events. * @return this * @throws InterruptedException if the current thread is interrupted while waiting */ @SuppressWarnings("unchecked") + @NonNull public final U await() throws InterruptedException { if (done.getCount() == 0) { return (U)this; @@ -146,14 +154,14 @@ public final U await() throws InterruptedException { } /** - * Awaits the specified amount of time or until this TestObserver/TestSubscriber - * receives an onError or onComplete events, whichever happens first. + * Awaits the specified amount of time or until this {@code TestObserver}/{@code TestSubscriber} + * receives an {@code onError} or {@code onComplete} events, whichever happens first. * @param time the waiting time * @param unit the time unit of the waiting time - * @return true if the TestObserver/TestSubscriber terminated, false if timeout happened + * @return true if the {@code TestObserver}/{@code TestSubscriber} terminated, false if timeout happened * @throws InterruptedException if the current thread is interrupted while waiting */ - public final boolean await(long time, TimeUnit unit) throws InterruptedException { + public final boolean await(long time, @NonNull TimeUnit unit) throws InterruptedException { boolean d = done.getCount() == 0 || (done.await(time, unit)); timeout = !d; return d; @@ -162,10 +170,11 @@ public final boolean await(long time, TimeUnit unit) throws InterruptedException // assertion methods /** - * Assert that this TestObserver/TestSubscriber received exactly one onComplete event. + * Assert that this {@code TestObserver}/{@code TestSubscriber} received exactly one {@code onComplete} event. * @return this */ @SuppressWarnings("unchecked") + @NonNull public final U assertComplete() { long c = completions; if (c == 0) { @@ -178,10 +187,11 @@ public final U assertComplete() { } /** - * Assert that this TestObserver/TestSubscriber has not received any onComplete event. + * Assert that this {@code TestObserver}/{@code TestSubscriber} has not received an {@code onComplete} event. * @return this */ @SuppressWarnings("unchecked") + @NonNull public final U assertNotComplete() { long c = completions; if (c == 1) { @@ -194,10 +204,11 @@ public final U assertNotComplete() { } /** - * Assert that this TestObserver/TestSubscriber has not received any onError event. + * Assert that this {@code TestObserver}/{@code TestSubscriber} has not received an {@code onError} event. * @return this */ @SuppressWarnings("unchecked") + @NonNull public final U assertNoErrors() { int s = errors.size(); if (s != 0) { @@ -207,9 +218,9 @@ public final U assertNoErrors() { } /** - * Assert that this TestObserver/TestSubscriber received exactly the specified onError event value. + * Assert that this {@code TestObserver}/{@code TestSubscriber} received exactly the specified {@code onError} event value. * - *

The comparison is performed via Objects.equals(); since most exceptions don't + *

The comparison is performed via {@link Objects#equals(Object, Object)}; since most exceptions don't * implement equals(), this assertion may fail. Use the {@link #assertError(Class)} * overload to test against the class of an error instead of an instance of an error * or {@link #assertError(Predicate)} to test with different condition. @@ -218,31 +229,39 @@ public final U assertNoErrors() { * @see #assertError(Class) * @see #assertError(Predicate) */ - public final U assertError(Throwable error) { - return assertError(Functions.equalsWith(error)); + @NonNull + public final U assertError(@NonNull Throwable error) { + return assertError(Functions.equalsWith(error), true); } /** - * Asserts that this TestObserver/TestSubscriber received exactly one onError event which is an - * instance of the specified errorClass class. - * @param errorClass the error class to expect + * Asserts that this {@code TestObserver}/{@code TestSubscriber} received exactly one {@code onError} event which is an + * instance of the specified {@code errorClass} {@link Class}. + * @param errorClass the error {@code Class} to expect * @return this */ - @SuppressWarnings({ "unchecked", "rawtypes", "cast" }) - public final U assertError(Class errorClass) { - return (U)assertError((Predicate)Functions.isInstanceOf(errorClass)); + @SuppressWarnings({ "unchecked", "rawtypes" }) + @NonNull + public final U assertError(@NonNull Class errorClass) { + return (U)assertError((Predicate)Functions.isInstanceOf(errorClass), true); } /** - * Asserts that this TestObserver/TestSubscriber received exactly one onError event for which - * the provided predicate returns true. + * Asserts that this {@code TestObserver}/{@code TestSubscriber} received exactly one {@code onError} event for which + * the provided predicate returns {@code true}. * @param errorPredicate - * the predicate that receives the error Throwable - * and should return true for expected errors. + * the predicate that receives the error {@link Throwable} + * and should return {@code true} for expected errors. * @return this */ + @NonNull + public final U assertError(@NonNull Predicate errorPredicate) { + return assertError(errorPredicate, false); + } + @SuppressWarnings("unchecked") - public final U assertError(Predicate errorPredicate) { + @NonNull + private U assertError(@NonNull Predicate errorPredicate, boolean exact) { int s = errors.size(); if (s == 0) { throw fail("No errors"); @@ -263,55 +282,63 @@ public final U assertError(Predicate errorPredicate) { if (found) { if (s != 1) { - throw fail("Error present but other errors as well"); + if (exact) { + throw fail("Error present but other errors as well"); + } + throw fail("One error passed the predicate but other errors are present as well"); } } else { - throw fail("Error not present"); + if (exact) { + throw fail("Error not present"); + } + throw fail("No error(s) passed the predicate"); } return (U)this; } /** - * Assert that this TestObserver/TestSubscriber received exactly one onNext value which is equal to - * the given value with respect to Objects.equals. + * Assert that this {@code TestObserver}/{@code TestSubscriber} received exactly one {@code onNext} value which is equal to + * the given value with respect to {@link Objects#equals(Object, Object)}. * @param value the value to expect * @return this */ @SuppressWarnings("unchecked") - public final U assertValue(T value) { + @NonNull + public final U assertValue(@NonNull T value) { int s = values.size(); if (s != 1) { - throw fail("expected: " + valueAndClass(value) + " but was: " + values); + throw fail("\nexpected: " + valueAndClass(value) + "\ngot: " + values); } T v = values.get(0); - if (!ObjectHelper.equals(value, v)) { - throw fail("expected: " + valueAndClass(value) + " but was: " + valueAndClass(v)); + if (!Objects.equals(value, v)) { + throw fail("\nexpected: " + valueAndClass(value) + "\ngot: " + valueAndClass(v)); } return (U)this; } /** - * Asserts that this TestObserver/TestSubscriber received exactly one onNext value for which - * the provided predicate returns true. + * Asserts that this {@code TestObserver}/{@code TestSubscriber} received exactly one {@code onNext} value for which + * the provided predicate returns {@code true}. * @param valuePredicate - * the predicate that receives the onNext value - * and should return true for the expected value. + * the predicate that receives the {@code onNext} value + * and should return {@code true} for the expected value. * @return this */ @SuppressWarnings("unchecked") - public final U assertValue(Predicate valuePredicate) { + @NonNull + public final U assertValue(@NonNull Predicate valuePredicate) { assertValueAt(0, valuePredicate); if (values.size() > 1) { - throw fail("Value present but other values as well"); + throw fail("The first value passed the predicate but this consumer received more than one value"); } return (U)this; } /** - * Asserts that this TestObserver/TestSubscriber received an onNext value at the given index - * which is equal to the given value with respect to null-safe Object.equals. + * Asserts that this {@code TestObserver}/{@code TestSubscriber} received an {@code onNext} value at the given index + * which is equal to the given value with respect to {@code null}-safe {@link Objects#equals(Object, Object)}. *

History: 2.1.3 - experimental * @param index the position to assert on * @param value the value to expect @@ -319,47 +346,51 @@ public final U assertValue(Predicate valuePredicate) { * @since 2.2 */ @SuppressWarnings("unchecked") - public final U assertValueAt(int index, T value) { + @NonNull + public final U assertValueAt(int index, @NonNull T value) { int s = values.size(); if (s == 0) { throw fail("No values"); } - if (index >= s) { - throw fail("Invalid index: " + index); + if (index < 0 || index >= s) { + throw fail("Index " + index + " is out of range [0, " + s + ")"); } T v = values.get(index); - if (!ObjectHelper.equals(value, v)) { - throw fail("expected: " + valueAndClass(value) + " but was: " + valueAndClass(v)); + if (!Objects.equals(value, v)) { + throw fail("\nexpected: " + valueAndClass(value) + "\ngot: " + valueAndClass(v) + + "; Value at position " + index + " differ"); } return (U)this; } /** - * Asserts that this TestObserver/TestSubscriber received an onNext value at the given index - * for the provided predicate returns true. + * Asserts that this {@code TestObserver}/{@code TestSubscriber} received an {@code onNext} value at the given index + * for the provided predicate returns {@code true}. * @param index the position to assert on * @param valuePredicate - * the predicate that receives the onNext value - * and should return true for the expected value. + * the predicate that receives the {@code onNext} value + * and should return {@code true} for the expected value. * @return this */ @SuppressWarnings("unchecked") - public final U assertValueAt(int index, Predicate valuePredicate) { + @NonNull + public final U assertValueAt(int index, @NonNull Predicate valuePredicate) { int s = values.size(); if (s == 0) { throw fail("No values"); } - if (index >= values.size()) { - throw fail("Invalid index: " + index); + if (index < 0 || index >= s) { + throw fail("Index " + index + " is out of range [0, " + s + ")"); } boolean found = false; + T v = values.get(index); try { - if (valuePredicate.test(values.get(index))) { + if (valuePredicate.test(v)) { found = true; } } catch (Throwable ex) { @@ -367,17 +398,18 @@ public final U assertValueAt(int index, Predicate valuePredicate) { } if (!found) { - throw fail("Value not present"); + throw fail("Value " + valueAndClass(v) + " at position " + index + " did not pass the predicate"); } return (U)this; } /** - * Appends the class name to a non-null value. + * Appends the class name to a non-{@code null} value or returns {@code "null"}. * @param o the object * @return the string representation */ - public static String valueAndClass(Object o) { + @NonNull + public static String valueAndClass(@Nullable Object o) { if (o != null) { return o + " (class: " + o.getClass().getSimpleName() + ")"; } @@ -385,57 +417,64 @@ public static String valueAndClass(Object o) { } /** - * Assert that this TestObserver/TestSubscriber received the specified number onNext events. - * @param count the expected number of onNext events + * Assert that this {@code TestObserver}/{@code TestSubscriber} received the specified number {@code onNext} events. + * @param count the expected number of {@code onNext} events * @return this */ @SuppressWarnings("unchecked") + @NonNull public final U assertValueCount(int count) { int s = values.size(); if (s != count) { - throw fail("Value counts differ; expected: " + count + " but was: " + s); + throw fail("\nexpected: " + count + "\ngot: " + s + "; Value counts differ"); } return (U)this; } /** - * Assert that this TestObserver/TestSubscriber has not received any onNext events. + * Assert that this {@code TestObserver}/{@code TestSubscriber} has not received any {@code onNext} events. * @return this */ + @NonNull public final U assertNoValues() { return assertValueCount(0); } /** - * Assert that the TestObserver/TestSubscriber received only the specified values in the specified order. + * Assert that the {@code TestObserver}/{@code TestSubscriber} received only the specified values in the specified order. * @param values the values expected * @return this */ @SuppressWarnings("unchecked") - public final U assertValues(T... values) { + @SafeVarargs + @NonNull + public final U assertValues(@NonNull T... values) { int s = this.values.size(); if (s != values.length) { - throw fail("Value count differs; expected: " + values.length + " " + Arrays.toString(values) - + " but was: " + s + " " + this.values); + throw fail("\nexpected: " + values.length + " " + Arrays.toString(values) + + "\ngot: " + s + " " + this.values + "; Value count differs"); } for (int i = 0; i < s; i++) { T v = this.values.get(i); T u = values[i]; - if (!ObjectHelper.equals(u, v)) { - throw fail("Values at position " + i + " differ; expected: " + valueAndClass(u) + " but was: " + valueAndClass(v)); + if (!Objects.equals(u, v)) { + throw fail("\nexpected: " + valueAndClass(u) + "\ngot: " + valueAndClass(v) + + "; Value at position " + i + " differ"); } } return (U)this; } /** - * Assert that the TestObserver/TestSubscriber received only the specified values in the specified order without terminating. + * Assert that the {@code TestObserver}/{@code TestSubscriber} received only the specified values in the specified order without terminating. *

History: 2.1.4 - experimental * @param values the values expected * @return this * @since 2.2 */ - public final U assertValuesOnly(T... values) { + @SafeVarargs + @NonNull + public final U assertValuesOnly(@NonNull T... values) { return assertSubscribed() .assertValues(values) .assertNoErrors() @@ -443,12 +482,13 @@ public final U assertValuesOnly(T... values) { } /** - * Assert that the TestObserver/TestSubscriber received only the specified sequence of values in the same order. + * Assert that the {@code TestObserver}/{@code TestSubscriber} received only the specified sequence of values in the same order. * @param sequence the sequence of expected values in order * @return this */ @SuppressWarnings("unchecked") - public final U assertValueSequence(Iterable sequence) { + @NonNull + public final U assertValueSequence(@NonNull Iterable sequence) { int i = 0; Iterator actualIterator = values.iterator(); Iterator expectedIterator = sequence.iterator(); @@ -465,8 +505,9 @@ public final U assertValueSequence(Iterable sequence) { T u = expectedIterator.next(); T v = actualIterator.next(); - if (!ObjectHelper.equals(u, v)) { - throw fail("Values at position " + i + " differ; expected: " + valueAndClass(u) + " but was: " + valueAndClass(v)); + if (!Objects.equals(u, v)) { + throw fail("\nexpected: " + valueAndClass(u) + "\ngot: " + valueAndClass(v) + + "; Value at position " + i + " differ"); } i++; } @@ -481,19 +522,22 @@ public final U assertValueSequence(Iterable sequence) { } /** - * Assert that the onSubscribe method was called exactly once. + * Assert that the {@code onSubscribe} method was called exactly once. * @return this */ + @NonNull protected abstract U assertSubscribed(); /** - * Assert that the upstream signalled the specified values in order and + * Assert that the upstream signaled the specified values in order and * completed normally. * @param values the expected values, asserted in order * @return this * @see #assertFailure(Class, Object...) */ - public final U assertResult(T... values) { + @SafeVarargs + @NonNull + public final U assertResult(@NonNull T... values) { return assertSubscribed() .assertValues(values) .assertNoErrors() @@ -501,13 +545,15 @@ public final U assertResult(T... values) { } /** - * Assert that the upstream signalled the specified values in order - * and then failed with a specific class or subclass of Throwable. - * @param error the expected exception (parent) class + * Assert that the upstream signaled the specified values in order + * and then failed with a specific class or subclass of {@link Throwable}. + * @param error the expected exception (parent) {@link Class} * @param values the expected values, asserted in order * @return this */ - public final U assertFailure(Class error, T... values) { + @SafeVarargs + @NonNull + public final U assertFailure(@NonNull Class error, @NonNull T... values) { return assertSubscribed() .assertValues(values) .assertError(error) @@ -516,14 +562,15 @@ public final U assertFailure(Class error, T... values) { /** * Awaits until the internal latch is counted down. - *

If the wait times out or gets interrupted, the TestObserver/TestSubscriber is cancelled. + *

If the wait times out or gets interrupted, the {@code TestObserver}/{@code TestSubscriber} is cancelled. * @param time the waiting time * @param unit the time unit of the waiting time * @return this - * @throws RuntimeException wrapping an InterruptedException if the wait is interrupted + * @throws RuntimeException wrapping an {@link InterruptedException} if the wait is interrupted */ @SuppressWarnings("unchecked") - public final U awaitDone(long time, TimeUnit unit) { + @NonNull + public final U awaitDone(long time, @NonNull TimeUnit unit) { try { if (!done.await(time, unit)) { timeout = true; @@ -537,9 +584,12 @@ public final U awaitDone(long time, TimeUnit unit) { } /** - * Assert that the TestObserver/TestSubscriber has received a Disposable but no other events. + * Assert that the {@code TestObserver}/{@code TestSubscriber} has received a + * {@link io.reactivex.rxjava3.disposables.Disposable Disposable}/{@link org.reactivestreams.Subscription Subscription} + * via {@code onSubscribe} but no other events. * @return this */ + @NonNull public final U assertEmpty() { return assertSubscribed() .assertNoValues() @@ -551,18 +601,19 @@ public final U assertEmpty() { * Set the tag displayed along with an assertion failure's * other state information. *

History: 2.0.7 - experimental - * @param tag the string to display (null won't print any tag) + * @param tag the string to display ({@code null} won't print any tag) * @return this * @since 2.1 */ @SuppressWarnings("unchecked") - public final U withTag(CharSequence tag) { + @NonNull + public final U withTag(@Nullable CharSequence tag) { this.tag = tag; return (U)this; } /** - * Await until the TestObserver/TestSubscriber receives the given + * Await until the {@code TestObserver}/{@code TestSubscriber} receives the given * number of items or terminates by sleeping 10 milliseconds at a time * up to 5000 milliseconds of timeout. *

History: 2.0.7 - experimental @@ -571,6 +622,7 @@ public final U withTag(CharSequence tag) { * @since 2.1 */ @SuppressWarnings("unchecked") + @NonNull public final U awaitCount(int atLeast) { long start = System.currentTimeMillis(); long timeoutMillis = 5000; diff --git a/src/main/java/io/reactivex/rxjava3/observers/DefaultObserver.java b/src/main/java/io/reactivex/rxjava3/observers/DefaultObserver.java index 42cd69e79c..29d1f16d00 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/DefaultObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/DefaultObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/observers/DisposableCompletableObserver.java b/src/main/java/io/reactivex/rxjava3/observers/DisposableCompletableObserver.java index 25926663d5..20bf6d59d5 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/DisposableCompletableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/DisposableCompletableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,7 +53,7 @@ */ public abstract class DisposableCompletableObserver implements CompletableObserver, Disposable { - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); @Override public final void onSubscribe(@NonNull Disposable d) { @@ -63,7 +63,7 @@ public final void onSubscribe(@NonNull Disposable d) { } /** - * Called once the single upstream Disposable is set via onSubscribe. + * Called once the single upstream {@link Disposable} is set via {@link #onSubscribe(Disposable)}. */ protected void onStart() { } diff --git a/src/main/java/io/reactivex/rxjava3/observers/DisposableMaybeObserver.java b/src/main/java/io/reactivex/rxjava3/observers/DisposableMaybeObserver.java index 66ac6a8620..6ea1d26e97 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/DisposableMaybeObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/DisposableMaybeObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** - * An abstract {@link MaybeObserver} that allows asynchronous cancellation by implementing Disposable. + * An abstract {@link MaybeObserver} that allows asynchronous cancellation by implementing {@link Disposable}. * *

All pre-implemented final methods are thread-safe. * @@ -62,7 +62,7 @@ */ public abstract class DisposableMaybeObserver implements MaybeObserver, Disposable { - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); @Override public final void onSubscribe(@NonNull Disposable d) { @@ -72,7 +72,7 @@ public final void onSubscribe(@NonNull Disposable d) { } /** - * Called once the single upstream Disposable is set via onSubscribe. + * Called once the single upstream {@link Disposable} is set via {@link #onSubscribe(Disposable)}. */ protected void onStart() { } diff --git a/src/main/java/io/reactivex/rxjava3/observers/DisposableObserver.java b/src/main/java/io/reactivex/rxjava3/observers/DisposableObserver.java index a15587317c..d4e6d5f5d8 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/DisposableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/DisposableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** - * An abstract {@link Observer} that allows asynchronous cancellation by implementing Disposable. + * An abstract {@link Observer} that allows asynchronous cancellation by implementing {@link Disposable}. * *

All pre-implemented final methods are thread-safe. * @@ -66,7 +66,7 @@ */ public abstract class DisposableObserver implements Observer, Disposable { - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); @Override public final void onSubscribe(@NonNull Disposable d) { diff --git a/src/main/java/io/reactivex/rxjava3/observers/DisposableSingleObserver.java b/src/main/java/io/reactivex/rxjava3/observers/DisposableSingleObserver.java index 6d8ce73fb0..9126332b21 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/DisposableSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/DisposableSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** - * An abstract {@link SingleObserver} that allows asynchronous cancellation by implementing Disposable. + * An abstract {@link SingleObserver} that allows asynchronous cancellation by implementing {@link Disposable}. * *

All pre-implemented final methods are thread-safe. * @@ -55,7 +55,7 @@ */ public abstract class DisposableSingleObserver implements SingleObserver, Disposable { - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); @Override public final void onSubscribe(@NonNull Disposable d) { @@ -65,7 +65,7 @@ public final void onSubscribe(@NonNull Disposable d) { } /** - * Called once the single upstream Disposable is set via onSubscribe. + * Called once the single upstream {@link Disposable} is set via {@link #onSubscribe(Disposable)}. */ protected void onStart() { } diff --git a/src/main/java/io/reactivex/rxjava3/observers/LambdaConsumerIntrospection.java b/src/main/java/io/reactivex/rxjava3/observers/LambdaConsumerIntrospection.java index 5d74ac5e8c..293847979e 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/LambdaConsumerIntrospection.java +++ b/src/main/java/io/reactivex/rxjava3/observers/LambdaConsumerIntrospection.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,8 +24,8 @@ public interface LambdaConsumerIntrospection { /** - * Returns true or false if a custom onError consumer has been provided. - * @return {@code true} if a custom onError consumer implementation was supplied. Returns {@code false} if the + * Returns {@code true} or {@code false} if a custom {@code onError} consumer has been provided. + * @return {@code true} if a custom {@code onError} consumer implementation was supplied. Returns {@code false} if the * implementation is missing an error consumer and thus using a throwing default implementation. */ boolean hasCustomOnError(); diff --git a/src/main/java/io/reactivex/rxjava3/observers/ResourceCompletableObserver.java b/src/main/java/io/reactivex/rxjava3/observers/ResourceCompletableObserver.java index 3a7ab322a6..339692d756 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/ResourceCompletableObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/ResourceCompletableObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,13 +13,13 @@ package io.reactivex.rxjava3.observers; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.CompletableObserver; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** @@ -74,20 +74,20 @@ */ public abstract class ResourceCompletableObserver implements CompletableObserver, Disposable { /** The active subscription. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); /** The resource composite, can never be null. */ private final ListCompositeDisposable resources = new ListCompositeDisposable(); /** - * Adds a resource to this ResourceObserver. + * Adds a resource to this {@code ResourceCompletableObserver}. * * @param resource the resource to add * - * @throws NullPointerException if resource is null + * @throws NullPointerException if resource is {@code null} */ public final void add(@NonNull Disposable resource) { - ObjectHelper.requireNonNull(resource, "resource is null"); + Objects.requireNonNull(resource, "resource is null"); resources.add(resource); } @@ -99,7 +99,7 @@ public final void onSubscribe(@NonNull Disposable d) { } /** - * Called once the upstream sets a Subscription on this ResourceObserver. + * Called once the upstream sets a {@link Disposable} on this {@code ResourceCompletableObserver}. * *

You can perform initialization at this moment. The default * implementation does nothing. @@ -109,10 +109,10 @@ protected void onStart() { /** * Cancels the main disposable (if any) and disposes the resources associated with - * this ResourceObserver (if any). + * this {@code ResourceCompletableObserver} (if any). * - *

This method can be called before the upstream calls onSubscribe at which - * case the main Disposable will be immediately disposed. + *

This method can be called before the upstream calls {@link #onSubscribe(Disposable)} at which + * case the main {@link Disposable} will be immediately disposed. */ @Override public final void dispose() { @@ -122,8 +122,8 @@ public final void dispose() { } /** - * Returns true if this ResourceObserver has been disposed/cancelled. - * @return true if this ResourceObserver has been disposed/cancelled + * Returns true if this {@code ResourceCompletableObserver} has been disposed/cancelled. + * @return true if this {@code ResourceCompletableObserver} has been disposed/cancelled */ @Override public final boolean isDisposed() { diff --git a/src/main/java/io/reactivex/rxjava3/observers/ResourceMaybeObserver.java b/src/main/java/io/reactivex/rxjava3/observers/ResourceMaybeObserver.java index f824d3c97a..e9df1c3edc 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/ResourceMaybeObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/ResourceMaybeObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,13 +13,13 @@ package io.reactivex.rxjava3.observers; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.MaybeObserver; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** @@ -84,20 +84,20 @@ */ public abstract class ResourceMaybeObserver implements MaybeObserver, Disposable { /** The active subscription. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); /** The resource composite, can never be null. */ private final ListCompositeDisposable resources = new ListCompositeDisposable(); /** - * Adds a resource to this ResourceObserver. + * Adds a resource to this {@code ResourceMaybeObserver}. * * @param resource the resource to add * - * @throws NullPointerException if resource is null + * @throws NullPointerException if resource is {@code null} */ public final void add(@NonNull Disposable resource) { - ObjectHelper.requireNonNull(resource, "resource is null"); + Objects.requireNonNull(resource, "resource is null"); resources.add(resource); } @@ -109,7 +109,7 @@ public final void onSubscribe(@NonNull Disposable d) { } /** - * Called once the upstream sets a Subscription on this ResourceObserver. + * Called once the upstream sets a {@link Disposable} on this {@code ResourceMaybeObserver}. * *

You can perform initialization at this moment. The default * implementation does nothing. @@ -119,10 +119,10 @@ protected void onStart() { /** * Cancels the main disposable (if any) and disposes the resources associated with - * this ResourceObserver (if any). + * this {@code ResourceMaybeObserver} (if any). * - *

This method can be called before the upstream calls onSubscribe at which - * case the main Disposable will be immediately disposed. + *

This method can be called before the upstream calls {@link #onSubscribe(Disposable)} at which + * case the main {@link Disposable} will be immediately disposed. */ @Override public final void dispose() { @@ -132,8 +132,8 @@ public final void dispose() { } /** - * Returns true if this ResourceObserver has been disposed/cancelled. - * @return true if this ResourceObserver has been disposed/cancelled + * Returns true if this {@code ResourceMaybeObserver} has been disposed/cancelled. + * @return true if this {@code ResourceMaybeObserver} has been disposed/cancelled */ @Override public final boolean isDisposed() { diff --git a/src/main/java/io/reactivex/rxjava3/observers/ResourceObserver.java b/src/main/java/io/reactivex/rxjava3/observers/ResourceObserver.java index 04f2959595..0238dd5a17 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/ResourceObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/ResourceObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,13 +13,13 @@ package io.reactivex.rxjava3.observers; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** @@ -82,20 +82,20 @@ */ public abstract class ResourceObserver implements Observer, Disposable { /** The active subscription. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); /** The resource composite, can never be null. */ private final ListCompositeDisposable resources = new ListCompositeDisposable(); /** - * Adds a resource to this ResourceObserver. + * Adds a resource to this {@code ResourceObserver}. * * @param resource the resource to add * - * @throws NullPointerException if resource is null + * @throws NullPointerException if resource is {@code null} */ public final void add(@NonNull Disposable resource) { - ObjectHelper.requireNonNull(resource, "resource is null"); + Objects.requireNonNull(resource, "resource is null"); resources.add(resource); } @@ -107,7 +107,7 @@ public final void onSubscribe(Disposable d) { } /** - * Called once the upstream sets a Subscription on this ResourceObserver. + * Called once the upstream sets a {@link Disposable} on this {@code ResourceObserver}. * *

You can perform initialization at this moment. The default * implementation does nothing. @@ -117,10 +117,10 @@ protected void onStart() { /** * Cancels the main disposable (if any) and disposes the resources associated with - * this ResourceObserver (if any). + * this {@code ResourceObserver} (if any). * - *

This method can be called before the upstream calls onSubscribe at which - * case the main Disposable will be immediately disposed. + *

This method can be called before the upstream calls {@link #onSubscribe(Disposable)} at which + * case the main {@link Disposable} will be immediately disposed. */ @Override public final void dispose() { @@ -130,8 +130,8 @@ public final void dispose() { } /** - * Returns true if this ResourceObserver has been disposed/cancelled. - * @return true if this ResourceObserver has been disposed/cancelled + * Returns true if this {@code ResourceObserver} has been disposed/cancelled. + * @return true if this {@code ResourceObserver} has been disposed/cancelled */ @Override public final boolean isDisposed() { diff --git a/src/main/java/io/reactivex/rxjava3/observers/ResourceSingleObserver.java b/src/main/java/io/reactivex/rxjava3/observers/ResourceSingleObserver.java index fe8075f75a..b1b9049524 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/ResourceSingleObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/ResourceSingleObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,13 +13,13 @@ package io.reactivex.rxjava3.observers; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.SingleObserver; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** @@ -77,20 +77,20 @@ */ public abstract class ResourceSingleObserver implements SingleObserver, Disposable { /** The active subscription. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); /** The resource composite, can never be null. */ private final ListCompositeDisposable resources = new ListCompositeDisposable(); /** - * Adds a resource to this ResourceObserver. + * Adds a resource to this {@code ResourceSingleObserver}. * * @param resource the resource to add * - * @throws NullPointerException if resource is null + * @throws NullPointerException if resource is {@code null} */ public final void add(@NonNull Disposable resource) { - ObjectHelper.requireNonNull(resource, "resource is null"); + Objects.requireNonNull(resource, "resource is null"); resources.add(resource); } @@ -102,7 +102,7 @@ public final void onSubscribe(@NonNull Disposable d) { } /** - * Called once the upstream sets a Subscription on this ResourceObserver. + * Called once the upstream sets a {@link Disposable} on this {@code ResourceSingleObserver}. * *

You can perform initialization at this moment. The default * implementation does nothing. @@ -112,10 +112,10 @@ protected void onStart() { /** * Cancels the main disposable (if any) and disposes the resources associated with - * this ResourceObserver (if any). + * this {@code ResourceSingleObserver} (if any). * - *

This method can be called before the upstream calls onSubscribe at which - * case the main Disposable will be immediately disposed. + *

This method can be called before the upstream calls {@link #onSubscribe(Disposable)} at which + * case the main {@link Disposable} will be immediately disposed. */ @Override public final void dispose() { @@ -125,8 +125,8 @@ public final void dispose() { } /** - * Returns true if this ResourceObserver has been disposed/cancelled. - * @return true if this ResourceObserver has been disposed/cancelled + * Returns true if this {@code ResourceSingleObserver} has been disposed/cancelled. + * @return true if this {@code ResourceSingleObserver} has been disposed/cancelled */ @Override public final boolean isDisposed() { diff --git a/src/main/java/io/reactivex/rxjava3/observers/SafeObserver.java b/src/main/java/io/reactivex/rxjava3/observers/SafeObserver.java index f7612378e2..4a72e5b80e 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/SafeObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/SafeObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.observers; import io.reactivex.rxjava3.annotations.NonNull; @@ -21,7 +22,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Wraps another Subscriber and ensures all onXXX methods conform the protocol + * Wraps another {@link Observer} and ensures all {@code onXXX} methods conform the protocol * (except the requirement for serialized access). * * @param the value type @@ -35,8 +36,8 @@ public final class SafeObserver implements Observer, Disposable { boolean done; /** - * Constructs a SafeObserver by wrapping the given actual Observer. - * @param downstream the actual Observer to wrap, not null (not validated) + * Constructs a {@code SafeObserver} by wrapping the given actual {@link Observer}. + * @param downstream the actual {@code Observer} to wrap, not {@code null} (not validated) */ public SafeObserver(@NonNull Observer downstream) { this.downstream = downstream; diff --git a/src/main/java/io/reactivex/rxjava3/observers/SerializedObserver.java b/src/main/java/io/reactivex/rxjava3/observers/SerializedObserver.java index a0ffe75cce..062a3b6abf 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/SerializedObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/SerializedObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.observers; import io.reactivex.rxjava3.annotations.NonNull; @@ -20,13 +21,14 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Serializes access to the onNext, onError and onComplete methods of another Observer. + * Serializes access to the {@link Observer#onNext(Object)}, {@link Observer#onError(Throwable)} and + * {@link Observer#onComplete()} methods of another {@link Observer}. * *

Note that {@link #onSubscribe(Disposable)} is not serialized in respect of the other methods so - * make sure the {@code onSubscribe()} is called with a non-null {@code Disposable} + * make sure the {@code onSubscribe()} is called with a non-null {@link Disposable} * before any of the other methods are called. * - *

The implementation assumes that the actual Observer's methods don't throw. + *

The implementation assumes that the actual {@code Observer}'s methods don't throw. * * @param the value type */ @@ -44,19 +46,19 @@ public final class SerializedObserver implements Observer, Disposable { volatile boolean done; /** - * Construct a SerializedObserver by wrapping the given actual Observer. - * @param downstream the actual Observer, not null (not verified) + * Construct a {@code SerializedObserver} by wrapping the given actual {@link Observer}. + * @param downstream the actual {@code Observer}, not {@code null} (not verified) */ public SerializedObserver(@NonNull Observer downstream) { this(downstream, false); } /** - * Construct a SerializedObserver by wrapping the given actual Observer and + * Construct a SerializedObserver by wrapping the given actual {@link Observer} and * optionally delaying the errors till all regular values have been emitted * from the internal buffer. - * @param actual the actual Observer, not null (not verified) - * @param delayError if true, errors are emitted after regular values have been emitted + * @param actual the actual {@code Observer}, not {@code null} (not verified) + * @param delayError if {@code true}, errors are emitted after regular values have been emitted */ public SerializedObserver(@NonNull Observer actual, boolean delayError) { this.downstream = actual; @@ -100,7 +102,7 @@ public void onNext(@NonNull T t) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(QUEUE_LINK_SIZE); + q = new AppendOnlyLinkedArrayList<>(QUEUE_LINK_SIZE); queue = q; } q.add(NotificationLite.next(t)); @@ -129,7 +131,7 @@ public void onError(@NonNull Throwable t) { done = true; AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(QUEUE_LINK_SIZE); + q = new AppendOnlyLinkedArrayList<>(QUEUE_LINK_SIZE); queue = q; } Object err = NotificationLite.error(t); @@ -167,7 +169,7 @@ public void onComplete() { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(QUEUE_LINK_SIZE); + q = new AppendOnlyLinkedArrayList<>(QUEUE_LINK_SIZE); queue = q; } q.add(NotificationLite.complete()); diff --git a/src/main/java/io/reactivex/rxjava3/observers/TestObserver.java b/src/main/java/io/reactivex/rxjava3/observers/TestObserver.java index eae4fe113e..8142747ec1 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/TestObserver.java +++ b/src/main/java/io/reactivex/rxjava3/observers/TestObserver.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,23 +10,29 @@ * 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 io.reactivex.rxjava3.observers; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; /** - * An Observer that records events and allows making assertions about them. + * An {@link Observer}, {@link MaybeObserver}, {@link SingleObserver} and + * {@link CompletableObserver} composite that can record events from + * {@link Observable}s, {@link Maybe}s, {@link Single}s and {@link Completable}s + * and allows making assertions about them. * - *

You can override the onSubscribe, onNext, onError, onComplete, onSuccess and - * cancel methods but not the others (this is by design). + *

You can override the {@link #onSubscribe(Disposable)}, {@link #onNext(Object)}, {@link #onError(Throwable)}, + * {@link #onComplete()} and {@link #onSuccess(Object)} methods but not the others (this is by design). * - *

The TestObserver implements Disposable for convenience where dispose calls cancel. + *

The {@code TestObserver} implements {@link Disposable} for convenience where dispose calls cancel. * * @param the value type + * @see io.reactivex.rxjava3.subscribers.TestSubscriber */ public class TestObserver extends BaseTestConsumer> @@ -35,25 +41,27 @@ public class TestObserver private final Observer downstream; /** Holds the current subscription if any. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); /** - * Constructs a non-forwarding TestObserver. + * Constructs a non-forwarding {@code TestObserver}. * @param the value type received - * @return the new TestObserver instance + * @return the new {@code TestObserver} instance */ + @NonNull public static TestObserver create() { - return new TestObserver(); + return new TestObserver<>(); } /** - * Constructs a forwarding TestObserver. + * Constructs a forwarding {@code TestObserver}. * @param the value type received - * @param delegate the actual Observer to forward events to - * @return the new TestObserver instance + * @param delegate the actual {@link Observer} to forward events to + * @return the new {@code TestObserver} instance */ - public static TestObserver create(Observer delegate) { - return new TestObserver(delegate); + @NonNull + public static TestObserver create(@NonNull Observer delegate) { + return new TestObserver<>(delegate); } /** @@ -64,15 +72,15 @@ public TestObserver() { } /** - * Constructs a forwarding TestObserver. - * @param downstream the actual Observer to forward events to + * Constructs a forwarding {@code TestObserver}. + * @param downstream the actual {@link Observer} to forward events to */ - public TestObserver(Observer downstream) { + public TestObserver(@NonNull Observer downstream) { this.downstream = downstream; } @Override - public void onSubscribe(Disposable d) { + public void onSubscribe(@NonNull Disposable d) { lastThread = Thread.currentThread(); if (d == null) { @@ -91,7 +99,7 @@ public void onSubscribe(Disposable d) { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { if (!checkSubscriptionOnce) { checkSubscriptionOnce = true; if (upstream.get() == null) { @@ -111,7 +119,7 @@ public void onNext(T t) { } @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { if (!checkSubscriptionOnce) { checkSubscriptionOnce = true; if (upstream.get() == null) { @@ -164,18 +172,19 @@ public final boolean isDisposed() { // state retrieval methods /** - * Returns true if this TestObserver received a subscription. - * @return true if this TestObserver received a subscription + * Returns true if this {@code TestObserver} received a subscription. + * @return true if this {@code TestObserver} received a subscription */ public final boolean hasSubscription() { return upstream.get() != null; } /** - * Assert that the onSubscribe method was called exactly once. - * @return this; + * Assert that the {@link #onSubscribe(Disposable)} method was called exactly once. + * @return this */ @Override + @NonNull protected final TestObserver assertSubscribed() { if (upstream.get() == null) { throw fail("Not subscribed!"); @@ -184,7 +193,7 @@ protected final TestObserver assertSubscribed() { } @Override - public void onSuccess(T value) { + public void onSuccess(@NonNull T value) { onNext(value); onComplete(); } diff --git a/src/main/java/io/reactivex/rxjava3/observers/package-info.java b/src/main/java/io/reactivex/rxjava3/observers/package-info.java index 89596573d4..09f56f3eb0 100644 --- a/src/main/java/io/reactivex/rxjava3/observers/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/observers/package-info.java @@ -1,24 +1,56 @@ -/** +/* * 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. */ /** - * Default wrappers and implementations for Observer-based consumer classes and interfaces, + * Default wrappers and implementations for observer-based consumer classes and interfaces, * including disposable and resource-tracking variants and - * the {@link io.reactivex.rxjava3.observers.TestObserver} that allows unit testing - * {@link io.reactivex.rxjava3.core.Observable}-, {@link io.reactivex.rxjava3.core.Single}-, {@link io.reactivex.rxjava3.core.Maybe}- - * and {@link io.reactivex.rxjava3.core.Completable}-based flows. + * the {@link io.reactivex.rxjava3.observers.TestObserver TestObserver} that allows unit testing + * {@link io.reactivex.rxjava3.core.Observable Observable}-, {@link io.reactivex.rxjava3.core.Single Single}-, + * {@link io.reactivex.rxjava3.core.Maybe Maybe}- and {@link io.reactivex.rxjava3.core.Completable Completable}-based flows. + *

+ * Available observer variants + *
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
The available observer types.
Reactive typeBase interfaceSimpleDisposableResource
{@link io.reactivex.rxjava3.core.Observable Observable}{@link io.reactivex.rxjava3.core.Observer Observer}{@link io.reactivex.rxjava3.observers.DefaultObserver DefaultObserver}{@link io.reactivex.rxjava3.observers.DisposableObserver DisposableObserver}{@link io.reactivex.rxjava3.observers.ResourceObserver DisposableObserver}
{@link io.reactivex.rxjava3.core.Maybe Maybe}{@link io.reactivex.rxjava3.core.MaybeObserver MaybeObserver}N/A{@link io.reactivex.rxjava3.observers.DisposableMaybeObserver DisposableMaybeObserver}{@link io.reactivex.rxjava3.observers.ResourceMaybeObserver DisposableMaybeObserver}
{@link io.reactivex.rxjava3.core.Single Single}{@link io.reactivex.rxjava3.core.SingleObserver SingleObserver}N/A{@link io.reactivex.rxjava3.observers.DisposableSingleObserver DisposableSingleObserver}{@link io.reactivex.rxjava3.observers.ResourceSingleObserver DisposableSingleObserver}
{@link io.reactivex.rxjava3.core.Completable Completable}{@link io.reactivex.rxjava3.core.CompletableObserver CompletableObserver}N/A{@link io.reactivex.rxjava3.observers.DisposableCompletableObserver DisposableCompletableObserver}{@link io.reactivex.rxjava3.observers.ResourceCompletableObserver DisposableCompletableObserver}
*/ package io.reactivex.rxjava3.observers; diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/ConditionalSubscriber.java b/src/main/java/io/reactivex/rxjava3/operators/ConditionalSubscriber.java similarity index 73% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/ConditionalSubscriber.java rename to src/main/java/io/reactivex/rxjava3/operators/ConditionalSubscriber.java index 2b4fe68be1..0880d12544 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/ConditionalSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/operators/ConditionalSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -11,26 +11,27 @@ * the License for the specific language governing permissions and limitations under the License. */ -package io.reactivex.rxjava3.internal.fuseable; +package io.reactivex.rxjava3.operators; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.FlowableSubscriber; /** - * A Subscriber with an additional {@link #tryOnNext(Object)} method that - * tells the caller the specified value has been accepted or - * not. + * A {@link FlowableSubscriber} with an additional {@link #tryOnNext(Object)} method that + * tells the caller the specified value has been accepted or not. * *

This allows certain queue-drain or source-drain operators * to avoid requesting 1 on behalf of a dropped value. * * @param the value type + * @since 3.1.1 */ -public interface ConditionalSubscriber extends FlowableSubscriber { +public interface ConditionalSubscriber<@NonNull T> extends FlowableSubscriber { /** * Conditionally takes the value. * @param t the value to deliver * @return true if the value has been accepted, false if the value has been rejected * and the next value can be sent immediately */ - boolean tryOnNext(T t); + boolean tryOnNext(@NonNull T t); } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueDisposable.java b/src/main/java/io/reactivex/rxjava3/operators/QueueDisposable.java similarity index 65% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueDisposable.java rename to src/main/java/io/reactivex/rxjava3/operators/QueueDisposable.java index 2becb58d43..97096064d9 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueDisposable.java +++ b/src/main/java/io/reactivex/rxjava3/operators/QueueDisposable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,15 +10,15 @@ * 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 io.reactivex.rxjava3.internal.fuseable; -import java.util.Queue; +package io.reactivex.rxjava3.operators; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.disposables.Disposable; /** - * An interface extending Queue and Disposable and allows negotiating - * the fusion mode between subsequent operators of the {@code Observable} base reactive type. + * An interface extending {@link SimpleQueue} and {@link Disposable} and allows negotiating + * the fusion mode between subsequent operators of the {@link io.reactivex.rxjava3.core.Observable Observable} base reactive type. *

* The negotiation happens in subscription time when the upstream * calls the {@code onSubscribe} with an instance of this interface. The @@ -26,30 +26,32 @@ * with the appropriate mode before calling {@code request()}. *

* In synchronous fusion, all upstream values are either already available or is generated - * when {@link #poll()} is called synchronously. When the {@link #poll()} returns null, + * when {@link #poll()} is called synchronously. When the {@link #poll()} returns {@code null}, * that is the indication if a terminated stream. In this mode, the upstream won't call the onXXX methods. *

* In asynchronous fusion, upstream values may become available to {@link #poll()} eventually. - * Upstream signals onError() and onComplete() as usual but onNext may not actually contain - * the upstream value but have {@code null} instead. Downstream should treat such onNext as indication - * that {@link #poll()} can be called. + * Upstream signals {@code onError()} and {@code onComplete()} as usual, however, + * {@code onNext} will be called with {@code null} instead of the actual value. + * Downstream should treat such onNext as indication that {@link #poll()} can be called. *

- * The general rules for consuming the {@link Queue} interface: + * The general rules for consuming the {@link SimpleQueue} interface: *

    - *
  • {@link #poll()} has to be called sequentially (from within a serializing drain-loop).
  • + *
  • {@link #poll()} and {@link #clear()} has to be called sequentially (from within a serializing drain-loop).
  • *
  • In addition, callers of {@link #poll()} should be prepared to catch exceptions.
  • *
  • Due to how computation attaches to the {@link #poll()}, {@link #poll()} may return * {@code null} even if a preceding {@link #isEmpty()} returned false.
  • *
*

* Implementations should only allow calling the following methods and the rest of the - * {@link Queue} interface methods should throw {@link UnsupportedOperationException}: + * {@link SimpleQueue} interface methods should throw {@link UnsupportedOperationException}: *

    *
  • {@link #poll()}
  • *
  • {@link #isEmpty()}
  • *
  • {@link #clear()}
  • *
* @param the value type transmitted through the queue + * @see QueueSubscription + * @since 3.1.1 */ -public interface QueueDisposable extends QueueFuseable, Disposable { +public interface QueueDisposable<@NonNull T> extends QueueFuseable, Disposable { } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueFuseable.java b/src/main/java/io/reactivex/rxjava3/operators/QueueFuseable.java similarity index 92% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueFuseable.java rename to src/main/java/io/reactivex/rxjava3/operators/QueueFuseable.java index fadc824cc7..d295d12f0d 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueFuseable.java +++ b/src/main/java/io/reactivex/rxjava3/operators/QueueFuseable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -11,13 +11,16 @@ * the License for the specific language governing permissions and limitations under the License. */ -package io.reactivex.rxjava3.internal.fuseable; +package io.reactivex.rxjava3.operators; + +import io.reactivex.rxjava3.annotations.NonNull; /** - * Represents a SimpleQueue plus the means and constants for requesting a fusion mode. + * Represents a {@link SimpleQueue} plus the means and constants for requesting a fusion mode. * @param the value type returned by the SimpleQueue.poll() + * @since 3.1.1 */ -public interface QueueFuseable extends SimpleQueue { +public interface QueueFuseable<@NonNull T> extends SimpleQueue { /** * Returned by the {@link #requestFusion(int)} if the upstream doesn't support * the requested mode. diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueSubscription.java b/src/main/java/io/reactivex/rxjava3/operators/QueueSubscription.java similarity index 65% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueSubscription.java rename to src/main/java/io/reactivex/rxjava3/operators/QueueSubscription.java index 87599a6e40..eae8922992 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/QueueSubscription.java +++ b/src/main/java/io/reactivex/rxjava3/operators/QueueSubscription.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,15 +10,16 @@ * 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 io.reactivex.rxjava3.internal.fuseable; -import java.util.Queue; +package io.reactivex.rxjava3.operators; import org.reactivestreams.Subscription; +import io.reactivex.rxjava3.annotations.NonNull; + /** - * An interface extending Queue and Subscription and allows negotiating - * the fusion mode between subsequent operators of the {@code Flowable} base reactive type. + * An interface extending {@link SimpleQueue} and {@link Subscription} and allows negotiating + * the fusion mode between subsequent operators of the {@link io.reactivex.rxjava3.core.Flowable Flowable} base reactive type. *

* The negotiation happens in subscription time when the upstream * calls the {@code onSubscribe} with an instance of this interface. The @@ -31,27 +32,30 @@ * in this mode. In this mode, the upstream won't call the onXXX methods. *

* In asynchronous fusion, upstream values may become available to {@link #poll()} eventually. - * Upstream signals onError() and onComplete() as usual but onNext may not actually contain - * the upstream value but have {@code null} instead. Downstream should treat such onNext as indication - * that {@link #poll()} can be called. In this mode, the downstream still has to call {@link #request(long)} + * Upstream signals {@code onError()} and {@code onComplete()} as usual, however, + * {@code onNext} will be called with {@code null} instead of the actual value. + * Downstream should treat such onNext as indication that {@link #poll()} can be called. + * In this mode, the downstream still has to call {@link #request(long)} * to indicate it is prepared to receive more values. *

- * The general rules for consuming the {@link Queue} interface: + * The general rules for consuming the {@link SimpleQueue} interface: *

    - *
  • {@link #poll()} has to be called sequentially (from within a serializing drain-loop).
  • + *
  • {@link #poll()} and {@link #clear()} has to be called sequentially (from within a serializing drain-loop).
  • *
  • In addition, callers of {@link #poll()} should be prepared to catch exceptions.
  • *
  • Due to how computation attaches to the {@link #poll()}, {@link #poll()} may return * {@code null} even if a preceding {@link #isEmpty()} returned false.
  • *
*

* Implementations should only allow calling the following methods and the rest of the - * {@link Queue} interface methods should throw {@link UnsupportedOperationException}: + * {@link SimpleQueue} interface methods should throw {@link UnsupportedOperationException}: *

    *
  • {@link #poll()}
  • *
  • {@link #isEmpty()}
  • *
  • {@link #clear()}
  • *
* @param the value type transmitted through the queue + * @see QueueDisposable + * @since 3.1.1 */ -public interface QueueSubscription extends QueueFuseable, Subscription { +public interface QueueSubscription<@NonNull T> extends QueueFuseable, Subscription { } diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/ScalarSupplier.java b/src/main/java/io/reactivex/rxjava3/operators/ScalarSupplier.java similarity index 87% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/ScalarSupplier.java rename to src/main/java/io/reactivex/rxjava3/operators/ScalarSupplier.java index 039d730be6..9b79c5c102 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/ScalarSupplier.java +++ b/src/main/java/io/reactivex/rxjava3/operators/ScalarSupplier.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,8 +10,10 @@ * 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 io.reactivex.rxjava3.internal.fuseable; +package io.reactivex.rxjava3.operators; + +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.functions.Supplier; /** @@ -28,8 +30,10 @@ * single-element sources uniformly. *

* @param the scalar value type held by the implementing reactive type + * @since 3.1.1 */ -public interface ScalarSupplier extends Supplier { +@FunctionalInterface +public interface ScalarSupplier<@NonNull T> extends Supplier { // overridden to remove the throws Throwable @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/SimplePlainQueue.java b/src/main/java/io/reactivex/rxjava3/operators/SimplePlainQueue.java similarity index 72% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/SimplePlainQueue.java rename to src/main/java/io/reactivex/rxjava3/operators/SimplePlainQueue.java index 3708580270..7e0cac21f3 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/SimplePlainQueue.java +++ b/src/main/java/io/reactivex/rxjava3/operators/SimplePlainQueue.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -11,16 +11,17 @@ * the License for the specific language governing permissions and limitations under the License. */ -package io.reactivex.rxjava3.internal.fuseable; +package io.reactivex.rxjava3.operators; -import io.reactivex.rxjava3.annotations.Nullable; +import io.reactivex.rxjava3.annotations.*; /** - * Override of the SimpleQueue interface with no throws Exception on poll(). + * Override of the {@link SimpleQueue} interface with no {@code throws Throwable} on {@code poll()}. * * @param the value type to offer and poll, not null + * @since 3.1.1 */ -public interface SimplePlainQueue extends SimpleQueue { +public interface SimplePlainQueue<@NonNull T> extends SimpleQueue { @Nullable @Override diff --git a/src/main/java/io/reactivex/rxjava3/internal/fuseable/SimpleQueue.java b/src/main/java/io/reactivex/rxjava3/operators/SimpleQueue.java similarity index 83% rename from src/main/java/io/reactivex/rxjava3/internal/fuseable/SimpleQueue.java rename to src/main/java/io/reactivex/rxjava3/operators/SimpleQueue.java index 29af5299d8..a10b7f9a43 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/fuseable/SimpleQueue.java +++ b/src/main/java/io/reactivex/rxjava3/operators/SimpleQueue.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -11,19 +11,24 @@ * the License for the specific language governing permissions and limitations under the License. */ -package io.reactivex.rxjava3.internal.fuseable; +package io.reactivex.rxjava3.operators; import io.reactivex.rxjava3.annotations.*; /** - * A minimalist queue interface without the method bloat of java.util.Collection and java.util.Queue. + * A simplified interface for offering, polling and clearing a queue. + *

+ * This interface does not define most of the {@link java.util.Collection} + * or {@link java.util.Queue} methods as the intended usage of {@code SimpleQueue} + * does not require support for iteration or introspection. * * @param the value type to offer and poll, not null + * @since 3.1.1 */ -public interface SimpleQueue { +public interface SimpleQueue<@NonNull T> { /** - * Atomically enqueue a single. + * Atomically enqueue a single value. * @param value the value to enqueue, not null * @return true if successful, false if the value was not enqueued * likely due to reaching the queue capacity) diff --git a/src/main/java/io/reactivex/rxjava3/internal/queue/SpscArrayQueue.java b/src/main/java/io/reactivex/rxjava3/operators/SpscArrayQueue.java similarity index 93% rename from src/main/java/io/reactivex/rxjava3/internal/queue/SpscArrayQueue.java rename to src/main/java/io/reactivex/rxjava3/operators/SpscArrayQueue.java index c86a109f81..1e4c2d4f11 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/queue/SpscArrayQueue.java +++ b/src/main/java/io/reactivex/rxjava3/operators/SpscArrayQueue.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,12 +16,11 @@ * https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/atomic */ -package io.reactivex.rxjava3.internal.queue; +package io.reactivex.rxjava3.operators; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; import io.reactivex.rxjava3.internal.util.Pow2; /** @@ -37,6 +36,7 @@ * This implementation is wait free. * * @param the element type of the queue + * @since 3.1.1 */ public final class SpscArrayQueue extends AtomicReferenceArray implements SimplePlainQueue { private static final long serialVersionUID = -1296597691183856449L; @@ -47,6 +47,12 @@ public final class SpscArrayQueue extends AtomicReferenceArray implements final AtomicLong consumerIndex; final int lookAheadStep; + /** + * Constructs an array-backed queue with the given capacity rounded + * up to the next power of 2 size. + * @param capacity the maximum number of elements the queue would hold, + * rounded up to the next power of 2 + */ public SpscArrayQueue(int capacity) { super(Pow2.roundToPowerOfTwo(capacity)); this.mask = length() - 1; diff --git a/src/main/java/io/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue.java b/src/main/java/io/reactivex/rxjava3/operators/SpscLinkedArrayQueue.java similarity index 93% rename from src/main/java/io/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue.java rename to src/main/java/io/reactivex/rxjava3/operators/SpscLinkedArrayQueue.java index 4a2a182dfc..97fac2253a 100644 --- a/src/main/java/io/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue.java +++ b/src/main/java/io/reactivex/rxjava3/operators/SpscLinkedArrayQueue.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,18 +16,18 @@ * https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/atomic */ -package io.reactivex.rxjava3.internal.queue; +package io.reactivex.rxjava3.operators; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.internal.fuseable.SimplePlainQueue; import io.reactivex.rxjava3.internal.util.Pow2; /** * A single-producer single-consumer array-backed queue which can allocate new arrays in case the consumer is slower * than the producer. * @param the contained value type + * @since 3.1.1 */ public final class SpscLinkedArrayQueue implements SimplePlainQueue { static final int MAX_LOOK_AHEAD_STEP = Integer.getInteger("jctools.spsc.max.lookahead.step", 4096); @@ -45,10 +45,15 @@ public final class SpscLinkedArrayQueue implements SimplePlainQueue { private static final Object HAS_NEXT = new Object(); + /** + * Constructs a linked array-based queue instance with the given + * island size rounded up to the next power of 2. + * @param bufferSize the maximum number of elements per island + */ public SpscLinkedArrayQueue(final int bufferSize) { int p2capacity = Pow2.roundToPowerOfTwo(Math.max(8, bufferSize)); int mask = p2capacity - 1; - AtomicReferenceArray buffer = new AtomicReferenceArray(p2capacity + 1); + AtomicReferenceArray buffer = new AtomicReferenceArray<>(p2capacity + 1); producerBuffer = buffer; producerMask = mask; adjustLookAheadStep(p2capacity); @@ -100,7 +105,7 @@ private boolean writeToQueue(final AtomicReferenceArray buffer, final T private void resize(final AtomicReferenceArray oldBuffer, final long currIndex, final int offset, final T e, final long mask) { final int capacity = oldBuffer.length(); - final AtomicReferenceArray newBuffer = new AtomicReferenceArray(capacity); + final AtomicReferenceArray newBuffer = new AtomicReferenceArray<>(capacity); producerBuffer = newBuffer; producerLookAhead = currIndex + mask - 1; soElement(newBuffer, offset, e); // StoreStore @@ -160,7 +165,13 @@ private T newBufferPoll(AtomicReferenceArray nextBuffer, final long inde return n; } + /** + * Returns the next element in this queue without removing it or {@code null} + * if this queue is empty + * @return the next element or {@code null} + */ @SuppressWarnings("unchecked") + @Nullable public T peek() { final AtomicReferenceArray buffer = consumerBuffer; final long index = lpConsumerIndex(); @@ -186,6 +197,10 @@ public void clear() { while (poll() != null || !isEmpty()) { } // NOPMD } + /** + * Returns the number of elements in the queue. + * @return the number of elements in the queue + */ public int size() { /* * It is possible for a thread to be interrupted or reschedule between the read of the producer and @@ -247,7 +262,7 @@ private static void soElement(AtomicReferenceArray buffer, int offset, O buffer.lazySet(offset, e); } - private static Object lvElement(AtomicReferenceArray buffer, int offset) { + private static Object lvElement(AtomicReferenceArray buffer, int offset) { return buffer.get(offset); } @@ -273,7 +288,7 @@ public boolean offer(T first, T second) { soProducerIndex(p + 2); } else { final int capacity = buffer.length(); - final AtomicReferenceArray newBuffer = new AtomicReferenceArray(capacity); + final AtomicReferenceArray newBuffer = new AtomicReferenceArray<>(capacity); producerBuffer = newBuffer; pi = calcWrappedOffset(p, m); diff --git a/src/main/java/io/reactivex/rxjava3/operators/package-info.java b/src/main/java/io/reactivex/rxjava3/operators/package-info.java new file mode 100644 index 0000000000..93e1ca05be --- /dev/null +++ b/src/main/java/io/reactivex/rxjava3/operators/package-info.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +/** + * Classes and interfaces for writing advanced operators within and outside RxJava. + */ + +package io.reactivex.rxjava3.operators; \ No newline at end of file diff --git a/src/main/java/io/reactivex/rxjava3/parallel/ParallelFailureHandling.java b/src/main/java/io/reactivex/rxjava3/parallel/ParallelFailureHandling.java index 9e78d698e8..ce0496651e 100644 --- a/src/main/java/io/reactivex/rxjava3/parallel/ParallelFailureHandling.java +++ b/src/main/java/io/reactivex/rxjava3/parallel/ParallelFailureHandling.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowable.java b/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowable.java index 38d3505337..8d5414d27c 100644 --- a/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowable.java +++ b/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,59 +14,75 @@ package io.reactivex.rxjava3.parallel; import java.util.*; +import java.util.stream.*; import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.MissingBackpressureException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.*; +import io.reactivex.rxjava3.internal.jdk8.*; import io.reactivex.rxjava3.internal.operators.parallel.*; import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Abstract base class for Parallel publishers that take an array of Subscribers. + * Abstract base class for parallel publishing of events signaled to an array of {@link Subscriber}s. *

- * Use {@code from()} to start processing a regular Publisher in 'rails'. - * Use {@code runOn()} to introduce where each 'rail' should run on thread-vise. - * Use {@code sequential()} to merge the sources back into a single Flowable. + * Use {@link #from(Publisher)} to start processing a regular {@link Publisher} in 'rails'. + * Use {@link #runOn(Scheduler)} to introduce where each 'rail' should run on thread-vise. + * Use {@link #sequential()} to merge the sources back into a single {@link Flowable}. * *

History: 2.0.5 - experimental; 2.1 - beta * @param the value type * @since 2.2 */ -public abstract class ParallelFlowable { +public abstract class ParallelFlowable<@NonNull T> { /** - * Subscribes an array of Subscribers to this ParallelFlowable and triggers + * Subscribes an array of {@link Subscriber}s to this {@code ParallelFlowable} and triggers * the execution chain for all 'rails'. + *

+ *
Backpressure:
+ *
The backpressure behavior/expectation is determined by the supplied {@code Subscriber}.
+ *
Scheduler:
+ *
{@code subscribe} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param subscribers the subscribers array to run in parallel, the number - * of items must be equal to the parallelism level of this ParallelFlowable + * of items must be equal to the parallelism level of this {@code ParallelFlowable} + * @throws NullPointerException if {@code subscribers} is {@code null} * @see #parallelism() */ + @BackpressureSupport(BackpressureKind.SPECIAL) + @SchedulerSupport(SchedulerSupport.NONE) public abstract void subscribe(@NonNull Subscriber[] subscribers); /** - * Returns the number of expected parallel Subscribers. - * @return the number of expected parallel Subscribers + * Returns the number of expected parallel {@link Subscriber}s. + * @return the number of expected parallel {@code Subscriber}s */ + @CheckReturnValue public abstract int parallelism(); /** - * Validates the number of subscribers and returns true if their number - * matches the parallelism level of this ParallelFlowable. + * Validates the number of subscribers and returns {@code true} if their number + * matches the parallelism level of this {@code ParallelFlowable}. * - * @param subscribers the array of Subscribers - * @return true if the number of subscribers equals to the parallelism level + * @param subscribers the array of {@link Subscriber}s + * @return {@code true} if the number of subscribers equals to the parallelism level + * @throws NullPointerException if {@code subscribers} is {@code null} + * @throws IllegalArgumentException if {@code subscribers.length} is different from {@link #parallelism()} */ - protected final boolean validate(@NonNull Subscriber[] subscribers) { + protected final boolean validate(@NonNull Subscriber<@NonNull ?>[] subscribers) { + Objects.requireNonNull(subscribers, "subscribers is null"); int p = parallelism(); if (subscribers.length != p) { Throwable iae = new IllegalArgumentException("parallelism = " + p + ", subscribers = " + subscribers.length); - for (Subscriber s : subscribers) { + for (Subscriber<@NonNull ?> s : subscribers) { EmptySubscription.error(iae, s); } return false; @@ -75,120 +91,198 @@ protected final boolean validate(@NonNull Subscriber[] subscribers) { } /** - * Take a Publisher and prepare to consume it on multiple 'rails' (number of CPUs) + * Take a {@link Publisher} and prepare to consume it on multiple 'rails' (number of CPUs) * in a round-robin fashion. + *
+ *
Backpressure:
+ *
The operator honors the backpressure of the parallel rails and + * requests {@link Flowable#bufferSize} amount from the upstream, followed + * by 75% of that amount requested after every 75% received.
+ *
Scheduler:
+ *
{@code from} does not operate by default on a particular {@link Scheduler}.
+ *
* @param the value type - * @param source the source Publisher - * @return the ParallelFlowable instance + * @param source the source {@code Publisher} + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code source} is {@code null} */ @CheckReturnValue - public static ParallelFlowable from(@NonNull Publisher source) { + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public static <@NonNull T> ParallelFlowable from(@NonNull Publisher source) { return from(source, Runtime.getRuntime().availableProcessors(), Flowable.bufferSize()); } /** - * Take a Publisher and prepare to consume it on parallelism number of 'rails' in a round-robin fashion. + * Take a {@link Publisher} and prepare to consume it on parallelism number of 'rails' in a round-robin fashion. + *
+ *
Backpressure:
+ *
The operator honors the backpressure of the parallel rails and + * requests {@link Flowable#bufferSize} amount from the upstream, followed + * by 75% of that amount requested after every 75% received.
+ *
Scheduler:
+ *
{@code from} does not operate by default on a particular {@link Scheduler}.
+ *
* @param the value type - * @param source the source Publisher + * @param source the source {@code Publisher} * @param parallelism the number of parallel rails - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code source} is {@code null} + * @throws IllegalArgumentException if {@code parallelism} is non-positive */ @CheckReturnValue - public static ParallelFlowable from(@NonNull Publisher source, int parallelism) { + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public static <@NonNull T> ParallelFlowable from(@NonNull Publisher source, int parallelism) { return from(source, parallelism, Flowable.bufferSize()); } /** - * Take a Publisher and prepare to consume it on parallelism number of 'rails' , + * Take a {@link Publisher} and prepare to consume it on parallelism number of 'rails' , * possibly ordered and round-robin fashion and use custom prefetch amount and queue - * for dealing with the source Publisher's values. + * for dealing with the source {@code Publisher}'s values. + *
+ *
Backpressure:
+ *
The operator honors the backpressure of the parallel rails and + * requests the {@code prefetch} amount from the upstream, followed + * by 75% of that amount requested after every 75% received.
+ *
Scheduler:
+ *
{@code from} does not operate by default on a particular {@link Scheduler}.
+ *
* @param the value type - * @param source the source Publisher + * @param source the source {@code Publisher} * @param parallelism the number of parallel rails * @param prefetch the number of values to prefetch from the source * the source until there is a rail ready to process it. - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code source} is {@code null} + * @throws IllegalArgumentException if {@code parallelism} or {@code prefetch} is non-positive */ @CheckReturnValue @NonNull - public static ParallelFlowable from(@NonNull Publisher source, + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.FULL) + public static <@NonNull T> ParallelFlowable from(@NonNull Publisher source, int parallelism, int prefetch) { - ObjectHelper.requireNonNull(source, "source"); + Objects.requireNonNull(source, "source is null"); ObjectHelper.verifyPositive(parallelism, "parallelism"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelFromPublisher(source, parallelism, prefetch)); + return RxJavaPlugins.onAssembly(new ParallelFromPublisher<>(source, parallelism, prefetch)); } /** * Maps the source values on each 'rail' to another value. *

- * Note that the same mapper function may be called from multiple threads concurrently. + * Note that the same {@code mapper} function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* @param the output value type - * @param mapper the mapper function turning Ts into Us. - * @return the new ParallelFlowable instance + * @param mapper the mapper function turning Ts into Rs. + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable map(@NonNull Function mapper) { - ObjectHelper.requireNonNull(mapper, "mapper"); - return RxJavaPlugins.onAssembly(new ParallelMap(this, mapper)); + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public final <@NonNull R> ParallelFlowable map(@NonNull Function mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ParallelMap<>(this, mapper)); } /** * Maps the source values on each 'rail' to another value and * handles errors based on the given {@link ParallelFailureHandling} enumeration value. *

- * Note that the same mapper function may be called from multiple threads concurrently. + * Note that the same {@code mapper} function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.0.8 - experimental * @param the output value type - * @param mapper the mapper function turning Ts into Us. + * @param mapper the mapper function turning Ts into Rs. * @param errorHandler the enumeration that defines how to handle errors thrown - * from the mapper function - * @return the new ParallelFlowable instance + * from the {@code mapper} function + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} or {@code errorHandler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull - public final ParallelFlowable map(@NonNull Function mapper, @NonNull ParallelFailureHandling errorHandler) { - ObjectHelper.requireNonNull(mapper, "mapper"); - ObjectHelper.requireNonNull(errorHandler, "errorHandler is null"); - return RxJavaPlugins.onAssembly(new ParallelMapTry(this, mapper, errorHandler)); + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public final <@NonNull R> ParallelFlowable map(@NonNull Function mapper, @NonNull ParallelFailureHandling errorHandler) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelMapTry<>(this, mapper, errorHandler)); } /** * Maps the source values on each 'rail' to another value and * handles errors based on the returned value by the handler function. *

- * Note that the same mapper function may be called from multiple threads concurrently. + * Note that the same {@code mapper} function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.0.8 - experimental * @param the output value type - * @param mapper the mapper function turning Ts into Us. + * @param mapper the mapper function turning Ts into Rs. * @param errorHandler the function called with the current repeat count and - * failure Throwable and should return one of the {@link ParallelFailureHandling} + * failure {@link Throwable} and should return one of the {@link ParallelFailureHandling} * enumeration values to indicate how to proceed. - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} or {@code errorHandler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull - public final ParallelFlowable map(@NonNull Function mapper, @NonNull BiFunction errorHandler) { - ObjectHelper.requireNonNull(mapper, "mapper"); - ObjectHelper.requireNonNull(errorHandler, "errorHandler is null"); - return RxJavaPlugins.onAssembly(new ParallelMapTry(this, mapper, errorHandler)); + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public final <@NonNull R> ParallelFlowable map(@NonNull Function mapper, @NonNull BiFunction errorHandler) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelMapTry<>(this, mapper, errorHandler)); } /** * Filters the source values on each 'rail'. *

* Note that the same predicate may be called from multiple threads concurrently. - * @param predicate the function returning true to keep a value or false to drop a value - * @return the new ParallelFlowable instance + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code filter} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param predicate the function returning {@code true} to keep a value or {@code false} to drop a value + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code predicate} is {@code null} */ @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) public final ParallelFlowable filter(@NonNull Predicate predicate) { - ObjectHelper.requireNonNull(predicate, "predicate"); - return RxJavaPlugins.onAssembly(new ParallelFilter(this, predicate)); + Objects.requireNonNull(predicate, "predicate is null"); + return RxJavaPlugins.onAssembly(new ParallelFilter<>(this, predicate)); } /** @@ -196,18 +290,29 @@ public final ParallelFlowable filter(@NonNull Predicate predicate) * handles errors based on the given {@link ParallelFailureHandling} enumeration value. *

* Note that the same predicate may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code filter} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.0.8 - experimental - * @param predicate the function returning true to keep a value or false to drop a value + * @param predicate the function returning {@code true} to keep a value or {@code false} to drop a value * @param errorHandler the enumeration that defines how to handle errors thrown - * from the predicate - * @return the new ParallelFlowable instance + * from the {@code predicate} + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code predicate} or {@code errorHandler} is {@code null} * @since 2.2 */ @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) public final ParallelFlowable filter(@NonNull Predicate predicate, @NonNull ParallelFailureHandling errorHandler) { - ObjectHelper.requireNonNull(predicate, "predicate"); - ObjectHelper.requireNonNull(errorHandler, "errorHandler is null"); - return RxJavaPlugins.onAssembly(new ParallelFilterTry(this, predicate, errorHandler)); + Objects.requireNonNull(predicate, "predicate is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelFilterTry<>(this, predicate, errorHandler)); } /** @@ -215,146 +320,208 @@ public final ParallelFlowable filter(@NonNull Predicate predicate, * handles errors based on the returned value by the handler function. *

* Note that the same predicate may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.0.8 - experimental - * @param predicate the function returning true to keep a value or false to drop a value + * @param predicate the function returning {@code true} to keep a value or {@code false} to drop a value * @param errorHandler the function called with the current repeat count and - * failure Throwable and should return one of the {@link ParallelFailureHandling} + * failure {@link Throwable} and should return one of the {@link ParallelFailureHandling} * enumeration values to indicate how to proceed. - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code predicate} or {@code errorHandler} is {@code null} * @since 2.2 */ @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) public final ParallelFlowable filter(@NonNull Predicate predicate, @NonNull BiFunction errorHandler) { - ObjectHelper.requireNonNull(predicate, "predicate"); - ObjectHelper.requireNonNull(errorHandler, "errorHandler is null"); - return RxJavaPlugins.onAssembly(new ParallelFilterTry(this, predicate, errorHandler)); + Objects.requireNonNull(predicate, "predicate is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelFilterTry<>(this, predicate, errorHandler)); } /** - * Specifies where each 'rail' will observe its incoming values with + * Specifies where each 'rail' will observe its incoming values, specified via a {@link Scheduler}, with * no work-stealing and default prefetch amount. *

- * This operator uses the default prefetch size returned by {@code Flowable.bufferSize()}. + * This operator uses the default prefetch size returned by {@link Flowable#bufferSize()}. *

- * The operator will call {@code Scheduler.createWorker()} as many - * times as this ParallelFlowable's parallelism level is. + * The operator will call {@link Scheduler#createWorker()} as many + * times as this {@code ParallelFlowable}'s parallelism level is. *

- * No assumptions are made about the Scheduler's parallelism level, - * if the Scheduler's parallelism level is lower than the ParallelFlowable's, + * No assumptions are made about the {@code Scheduler}'s parallelism level, + * if the {@code Scheduler}'s parallelism level is lower than the {@code ParallelFlowable}'s, * some rails may end up on the same thread/worker. *

- * This operator doesn't require the Scheduler to be trampolining as it + * This operator doesn't require the {@code Scheduler} to be trampolining as it * does its own built-in trampolining logic. + *

+ *
Backpressure:
+ *
The operator honors the backpressure of the parallel rails and + * requests {@link Flowable#bufferSize} amount from the upstream, followed + * by 75% of that amount requested after every 75% received.
+ *
Scheduler:
+ *
{@code runOn} drains the upstream rails on the specified {@code Scheduler}'s + * {@link io.reactivex.rxjava3.core.Scheduler.Worker Worker}s.
+ *
* * @param scheduler the scheduler to use - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.CUSTOM) public final ParallelFlowable runOn(@NonNull Scheduler scheduler) { return runOn(scheduler, Flowable.bufferSize()); } /** - * Specifies where each 'rail' will observe its incoming values with + * Specifies where each 'rail' will observe its incoming values, specified via a {@link Scheduler}, with * possibly work-stealing and a given prefetch amount. *

- * This operator uses the default prefetch size returned by {@code Flowable.bufferSize()}. + * This operator uses the default prefetch size returned by {@link Flowable#bufferSize()}. *

- * The operator will call {@code Scheduler.createWorker()} as many - * times as this ParallelFlowable's parallelism level is. + * The operator will call {@link Scheduler#createWorker()} as many + * times as this {@code ParallelFlowable}'s parallelism level is. *

- * No assumptions are made about the Scheduler's parallelism level, - * if the Scheduler's parallelism level is lower than the ParallelFlowable's, + * No assumptions are made about the {@code Scheduler}'s parallelism level, + * if the {@code Scheduler}'s parallelism level is lower than the {@code ParallelFlowable}'s, * some rails may end up on the same thread/worker. *

- * This operator doesn't require the Scheduler to be trampolining as it + * This operator doesn't require the {@code Scheduler} to be trampolining as it * does its own built-in trampolining logic. + *

+ *
Backpressure:
+ *
The operator honors the backpressure of the parallel rails and + * requests the {@code prefetch} amount from the upstream, followed + * by 75% of that amount requested after every 75% received.
+ *
Scheduler:
+ *
{@code runOn} drains the upstream rails on the specified {@code Scheduler}'s + * {@link io.reactivex.rxjava3.core.Scheduler.Worker Worker}s.
+ *
* * @param scheduler the scheduler to use * that rail's worker has run out of work. * @param prefetch the number of values to request on each 'rail' from the source - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.CUSTOM) public final ParallelFlowable runOn(@NonNull Scheduler scheduler, int prefetch) { - ObjectHelper.requireNonNull(scheduler, "scheduler"); + Objects.requireNonNull(scheduler, "scheduler is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelRunOn(this, scheduler, prefetch)); + return RxJavaPlugins.onAssembly(new ParallelRunOn<>(this, scheduler, prefetch)); } /** - * Reduces all values within a 'rail' and across 'rails' with a reducer function into a single - * sequential value. + * Reduces all values within a 'rail' and across 'rails' with a reducer function into one + * {@link Flowable} sequence. *

* Note that the same reducer function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream and consumes + * the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code reduce} does not operate by default on a particular {@link Scheduler}.
+ *
* @param reducer the function to reduce two values into one. - * @return the new Flowable instance emitting the reduced value or empty if the ParallelFlowable was empty + * @return the new {@code Flowable} instance emitting the reduced value or empty if the current {@code ParallelFlowable} is empty + * @throws NullPointerException if {@code reducer} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) public final Flowable reduce(@NonNull BiFunction reducer) { - ObjectHelper.requireNonNull(reducer, "reducer"); - return RxJavaPlugins.onAssembly(new ParallelReduceFull(this, reducer)); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new ParallelReduceFull<>(this, reducer)); } /** * Reduces all values within a 'rail' to a single value (with a possibly different type) via - * a reducer function that is initialized on each rail from an initialSupplier value. + * a reducer function that is initialized on each rail from an {@code initialSupplier} value. *

* Note that the same mapper function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and consumes + * the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code reduce} does not operate by default on a particular {@link Scheduler}.
+ *
* @param the reduced output type * @param initialSupplier the supplier for the initial value * @param reducer the function to reduce a previous output of reduce (or the initial value supplied) * with a current source value. - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code initialSupplier} or {@code reducer} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable reduce(@NonNull Supplier initialSupplier, @NonNull BiFunction reducer) { - ObjectHelper.requireNonNull(initialSupplier, "initialSupplier"); - ObjectHelper.requireNonNull(reducer, "reducer"); - return RxJavaPlugins.onAssembly(new ParallelReduce(this, initialSupplier, reducer)); + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable reduce(@NonNull Supplier initialSupplier, @NonNull BiFunction reducer) { + Objects.requireNonNull(initialSupplier, "initialSupplier is null"); + Objects.requireNonNull(reducer, "reducer is null"); + return RxJavaPlugins.onAssembly(new ParallelReduce<>(this, initialSupplier, reducer)); } /** * Merges the values from each 'rail' in a round-robin or same-order fashion and - * exposes it as a regular Publisher sequence, running with a default prefetch value + * exposes it as a regular {@link Flowable} sequence, running with a default prefetch value * for the rails. *

* This operator uses the default prefetch size returned by {@code Flowable.bufferSize()}. * *

*
Backpressure:
- *
The operator honors backpressure.
+ *
The operator honors backpressure from the downstream and + * requests {@link Flowable#bufferSize()} amount from each rail, then + * requests from each rail 75% of this amount after 75% received.
*
Scheduler:
*
{@code sequential} does not operate by default on a particular {@link Scheduler}.
*
- * @return the new Flowable instance + * @return the new {@code Flowable} instance * @see ParallelFlowable#sequential(int) * @see ParallelFlowable#sequentialDelayError() */ @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue + @NonNull public final Flowable sequential() { return sequential(Flowable.bufferSize()); } /** * Merges the values from each 'rail' in a round-robin or same-order fashion and - * exposes it as a regular Publisher sequence, running with a give prefetch value + * exposes it as a regular {@link Flowable} sequence, running with a give prefetch value * for the rails. * *
*
Backpressure:
- *
The operator honors backpressure.
+ *
The operator honors backpressure from the downstream and + * requests the {@code prefetch} amount from each rail, then + * requests from each rail 75% of this amount after 75% received.
*
Scheduler:
*
{@code sequential} does not operate by default on a particular {@link Scheduler}.
*
* @param prefetch the prefetch amount to use for each rail - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ParallelFlowable#sequential() * @see ParallelFlowable#sequentialDelayError(int) */ @@ -364,24 +531,26 @@ public final Flowable sequential() { @NonNull public final Flowable sequential(int prefetch) { ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelJoin(this, prefetch, false)); + return RxJavaPlugins.onAssembly(new ParallelJoin<>(this, prefetch, false)); } /** * Merges the values from each 'rail' in a round-robin or same-order fashion and - * exposes it as a regular Flowable sequence, running with a default prefetch value + * exposes it as a regular {@link Flowable} sequence, running with a default prefetch value * for the rails and delaying errors from all rails till all terminate. *

* This operator uses the default prefetch size returned by {@code Flowable.bufferSize()}. * *

*
Backpressure:
- *
The operator honors backpressure.
+ *
The operator honors backpressure from the downstream and + * requests {@link Flowable#bufferSize()} amount from each rail, then + * requests from each rail 75% of this amount after 75% received.
*
Scheduler:
*
{@code sequentialDelayError} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.7 - experimental - * @return the new Flowable instance + * @return the new {@code Flowable} instance * @see ParallelFlowable#sequentialDelayError(int) * @see ParallelFlowable#sequential() * @since 2.2 @@ -396,18 +565,21 @@ public final Flowable sequentialDelayError() { /** * Merges the values from each 'rail' in a round-robin or same-order fashion and - * exposes it as a regular Publisher sequence, running with a give prefetch value + * exposes it as a regular {@link Flowable} sequence, running with a give prefetch value * for the rails and delaying errors from all rails till all terminate. * *

*
Backpressure:
- *
The operator honors backpressure.
+ *
The operator honors backpressure from the downstream and + * requests the {@code prefetch} amount from each rail, then + * requests from each rail 75% of this amount after 75% received.
*
Scheduler:
*
{@code sequentialDelayError} does not operate by default on a particular {@link Scheduler}.
*
*

History: 2.0.7 - experimental * @param prefetch the prefetch amount to use for each rail - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws IllegalArgumentException if {@code prefetch} is non-positive * @see ParallelFlowable#sequential() * @see ParallelFlowable#sequentialDelayError() * @since 2.2 @@ -418,94 +590,146 @@ public final Flowable sequentialDelayError() { @NonNull public final Flowable sequentialDelayError(int prefetch) { ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelJoin(this, prefetch, true)); + return RxJavaPlugins.onAssembly(new ParallelJoin<>(this, prefetch, true)); } /** - * Sorts the 'rails' of this ParallelFlowable and returns a Publisher that sequentially + * Sorts the 'rails' of this {@code ParallelFlowable} and returns a {@link Flowable} that sequentially * picks the smallest next value from the rails. *

- * This operator requires a finite source ParallelFlowable. + * This operator requires a finite source {@code ParallelFlowable}. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream and + * consumes the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code sorted} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param comparator the comparator to use - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code comparator} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) public final Flowable sorted(@NonNull Comparator comparator) { return sorted(comparator, 16); } /** - * Sorts the 'rails' of this ParallelFlowable and returns a Publisher that sequentially + * Sorts the 'rails' of this {@code ParallelFlowable} and returns a {@link Flowable} that sequentially * picks the smallest next value from the rails. *

- * This operator requires a finite source ParallelFlowable. + * This operator requires a finite source {@code ParallelFlowable}. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream and + * consumes the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code sorted} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param comparator the comparator to use * @param capacityHint the expected number of total elements - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code comparator} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) public final Flowable sorted(@NonNull Comparator comparator, int capacityHint) { - ObjectHelper.requireNonNull(comparator, "comparator is null"); + Objects.requireNonNull(comparator, "comparator is null"); ObjectHelper.verifyPositive(capacityHint, "capacityHint"); int ch = capacityHint / parallelism() + 1; - ParallelFlowable> railReduced = reduce(Functions.createArrayList(ch), ListAddBiConsumer.instance()); - ParallelFlowable> railSorted = railReduced.map(new SorterFunction(comparator)); + ParallelFlowable> railReduced = reduce(Functions.createArrayList(ch), ListAddBiConsumer.instance()); + ParallelFlowable> railSorted = railReduced.map(new SorterFunction<>(comparator)); - return RxJavaPlugins.onAssembly(new ParallelSortedJoin(railSorted, comparator)); + return RxJavaPlugins.onAssembly(new ParallelSortedJoin<>(railSorted, comparator)); } /** - * Sorts the 'rails' according to the comparator and returns a full sorted list as a Publisher. + * Sorts the 'rails' according to the comparator and returns a full sorted {@link List} as a {@link Flowable}. *

- * This operator requires a finite source ParallelFlowable. + * This operator requires a finite source {@code ParallelFlowable}. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream and + * consumes the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param comparator the comparator to compare elements - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code comparator} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) public final Flowable> toSortedList(@NonNull Comparator comparator) { return toSortedList(comparator, 16); } /** - * Sorts the 'rails' according to the comparator and returns a full sorted list as a Publisher. + * Sorts the 'rails' according to the comparator and returns a full sorted {@link List} as a {@link Flowable}. *

- * This operator requires a finite source ParallelFlowable. + * This operator requires a finite source {@code ParallelFlowable}. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream and + * consumes the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code toSortedList} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param comparator the comparator to compare elements * @param capacityHint the expected number of total elements - * @return the new Flowable instance + * @return the new {@code Flowable} instance + * @throws NullPointerException if {@code comparator} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull - public final Flowable> toSortedList(@NonNull Comparator comparator, int capacityHint) { - ObjectHelper.requireNonNull(comparator, "comparator is null"); + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + public final Flowable<@NonNull List> toSortedList(@NonNull Comparator comparator, int capacityHint) { + Objects.requireNonNull(comparator, "comparator is null"); ObjectHelper.verifyPositive(capacityHint, "capacityHint"); int ch = capacityHint / parallelism() + 1; - ParallelFlowable> railReduced = reduce(Functions.createArrayList(ch), ListAddBiConsumer.instance()); - ParallelFlowable> railSorted = railReduced.map(new SorterFunction(comparator)); + ParallelFlowable> railReduced = reduce(Functions.createArrayList(ch), ListAddBiConsumer.instance()); + ParallelFlowable> railSorted = railReduced.map(new SorterFunction<>(comparator)); - Flowable> merged = railSorted.reduce(new MergerBiFunction(comparator)); + Flowable> merged = railSorted.reduce(new MergerBiFunction<>(comparator)); return RxJavaPlugins.onAssembly(merged); } /** * Call the specified consumer with the current element passing through any 'rail'. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onNext the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onNext} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doOnNext(@NonNull Consumer onNext) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onNext, "onNext is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, onNext, Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -520,52 +744,82 @@ public final ParallelFlowable doOnNext(@NonNull Consumer onNext) { /** * Call the specified consumer with the current element passing through any 'rail' and * handles errors based on the given {@link ParallelFailureHandling} enumeration value. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.0.8 - experimental * @param onNext the callback * @param errorHandler the enumeration that defines how to handle errors thrown - * from the onNext consumer - * @return the new ParallelFlowable instance + * from the {@code onNext} consumer + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onNext} or {@code errorHandler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doOnNext(@NonNull Consumer onNext, @NonNull ParallelFailureHandling errorHandler) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(errorHandler, "errorHandler is null"); - return RxJavaPlugins.onAssembly(new ParallelDoOnNextTry(this, onNext, errorHandler)); + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelDoOnNextTry<>(this, onNext, errorHandler)); } /** * Call the specified consumer with the current element passing through any 'rail' and * handles errors based on the returned value by the handler function. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.0.8 - experimental * @param onNext the callback * @param errorHandler the function called with the current repeat count and - * failure Throwable and should return one of the {@link ParallelFailureHandling} + * failure {@link Throwable} and should return one of the {@link ParallelFailureHandling} * enumeration values to indicate how to proceed. - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onNext} or {@code errorHandler} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doOnNext(@NonNull Consumer onNext, @NonNull BiFunction errorHandler) { - ObjectHelper.requireNonNull(onNext, "onNext is null"); - ObjectHelper.requireNonNull(errorHandler, "errorHandler is null"); - return RxJavaPlugins.onAssembly(new ParallelDoOnNextTry(this, onNext, errorHandler)); + Objects.requireNonNull(onNext, "onNext is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelDoOnNextTry<>(this, onNext, errorHandler)); } /** * Call the specified consumer with the current element passing through any 'rail' * after it has been delivered to downstream within the rail. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onAfterNext the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onAfterNext} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doAfterNext(@NonNull Consumer onAfterNext) { - ObjectHelper.requireNonNull(onAfterNext, "onAfterNext is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onAfterNext, "onAfterNext is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), onAfterNext, Functions.emptyConsumer(), @@ -579,15 +833,25 @@ public final ParallelFlowable doAfterNext(@NonNull Consumer onAfte /** * Call the specified consumer with the exception passing through any 'rail'. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onError the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onError} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable doOnError(@NonNull Consumer onError) { - ObjectHelper.requireNonNull(onError, "onError is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) + public final ParallelFlowable doOnError(@NonNull Consumer onError) { + Objects.requireNonNull(onError, "onError is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), Functions.emptyConsumer(), onError, @@ -600,16 +864,26 @@ public final ParallelFlowable doOnError(@NonNull Consumer onError) } /** - * Run the specified Action when a 'rail' completes. + * Run the specified {@link Action} when a 'rail' completes. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onComplete the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onComplete} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doOnComplete(@NonNull Action onComplete) { - ObjectHelper.requireNonNull(onComplete, "onComplete is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onComplete, "onComplete is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -622,16 +896,26 @@ public final ParallelFlowable doOnComplete(@NonNull Action onComplete) { } /** - * Run the specified Action when a 'rail' completes or signals an error. + * Run the specified {@link Action} when a 'rail' completes or signals an error. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onAfterTerminate the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onAfterTerminate} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doAfterTerminated(@NonNull Action onAfterTerminate) { - ObjectHelper.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onAfterTerminate, "onAfterTerminate is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -644,16 +928,26 @@ public final ParallelFlowable doAfterTerminated(@NonNull Action onAfterTermin } /** - * Call the specified callback when a 'rail' receives a Subscription from its upstream. + * Call the specified callback when a 'rail' receives a {@link Subscription} from its upstream. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onSubscribe the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onSubscribe} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doOnSubscribe(@NonNull Consumer onSubscribe) { - ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onSubscribe, "onSubscribe is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -667,15 +961,25 @@ public final ParallelFlowable doOnSubscribe(@NonNull Consumer + *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ * * * @param onRequest the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onRequest} is {@code null} */ @CheckReturnValue @NonNull + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) public final ParallelFlowable doOnRequest(@NonNull LongConsumer onRequest) { - ObjectHelper.requireNonNull(onRequest, "onRequest is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onRequest, "onRequest is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -688,16 +992,26 @@ public final ParallelFlowable doOnRequest(@NonNull LongConsumer onRequest) { } /** - * Run the specified Action when a 'rail' receives a cancellation. + * Run the specified {@link Action} when a 'rail' receives a cancellation. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param onCancel the callback - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code onCancel} is {@code null} */ + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) @CheckReturnValue @NonNull public final ParallelFlowable doOnCancel(@NonNull Action onCancel) { - ObjectHelper.requireNonNull(onCancel, "onCancel is null"); - return RxJavaPlugins.onAssembly(new ParallelPeek(this, + Objects.requireNonNull(onCancel, "onCancel is null"); + return RxJavaPlugins.onAssembly(new ParallelPeek<>(this, Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.emptyConsumer(), @@ -710,218 +1024,672 @@ public final ParallelFlowable doOnCancel(@NonNull Action onCancel) { } /** - * Collect the elements in each rail into a collection supplied via a collectionSupplier + * Collect the elements in each rail into a collection supplied via a {@code collectionSupplier} * and collected into with a collector action, emitting the collection at the end. + *
+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * consumes the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the collection type * @param collectionSupplier the supplier of the collection in each rail * @param collector the collector, taking the per-rail collection and the current item - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code collectionSupplier} or {@code collector} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable collect(@NonNull Supplier collectionSupplier, @NonNull BiConsumer collector) { - ObjectHelper.requireNonNull(collectionSupplier, "collectionSupplier is null"); - ObjectHelper.requireNonNull(collector, "collector is null"); - return RxJavaPlugins.onAssembly(new ParallelCollect(this, collectionSupplier, collector)); + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull C> ParallelFlowable collect(@NonNull Supplier collectionSupplier, @NonNull BiConsumer collector) { + Objects.requireNonNull(collectionSupplier, "collectionSupplier is null"); + Objects.requireNonNull(collector, "collector is null"); + return RxJavaPlugins.onAssembly(new ParallelCollect<>(this, collectionSupplier, collector)); } /** - * Wraps multiple Publishers into a ParallelFlowable which runs them + * Wraps multiple {@link Publisher}s into a {@code ParallelFlowable} which runs them * in parallel and unordered. + *
+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the value type * @param publishers the array of publishers - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code publishers} is {@code null} + * @throws IllegalArgumentException if {@code publishers} is an empty array */ @CheckReturnValue @NonNull - public static ParallelFlowable fromArray(@NonNull Publisher... publishers) { + @SafeVarargs + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) + public static <@NonNull T> ParallelFlowable fromArray(@NonNull Publisher... publishers) { + Objects.requireNonNull(publishers, "publishers is null"); if (publishers.length == 0) { throw new IllegalArgumentException("Zero publishers not supported"); } - return RxJavaPlugins.onAssembly(new ParallelFromArray(publishers)); + return RxJavaPlugins.onAssembly(new ParallelFromArray<>(publishers)); } /** * Calls the specified converter function during assembly time and returns its resulting value. *

* This allows fluent conversion to any other type. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by how the converter function composes over the upstream source.
+ *
Scheduler:
+ *
{@code to} does not operate by default on a particular {@link Scheduler}.
+ *
*

History: 2.1.7 - experimental * @param the resulting object type - * @param converter the function that receives the current ParallelFlowable instance and returns a value + * @param converter the function that receives the current {@code ParallelFlowable} instance and returns a value * @return the converted value - * @throws NullPointerException if converter is null + * @throws NullPointerException if {@code converter} is {@code null} * @since 2.2 */ @CheckReturnValue @NonNull - public final R to(@NonNull ParallelFlowableConverter converter) { - return ObjectHelper.requireNonNull(converter, "converter is null").apply(this); + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> R to(@NonNull ParallelFlowableConverter converter) { + return Objects.requireNonNull(converter, "converter is null").apply(this); } /** - * Allows composing operators, in assembly time, on top of this ParallelFlowable - * and returns another ParallelFlowable with composed features. + * Allows composing operators, in assembly time, on top of this {@code ParallelFlowable} + * and returns another {@code ParallelFlowable} with composed features. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by how the converter function composes over the upstream source.
+ *
Scheduler:
+ *
{@code compose} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the output value type - * @param composer the composer function from ParallelFlowable (this) to another ParallelFlowable - * @return the ParallelFlowable returned by the function + * @param composer the composer function from {@code ParallelFlowable} (this) to another {@code ParallelFlowable} + * @return the {@code ParallelFlowable} returned by the function + * @throws NullPointerException if {@code composer} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable compose(@NonNull ParallelTransformer composer) { - return RxJavaPlugins.onAssembly(ObjectHelper.requireNonNull(composer, "composer is null").apply(this)); + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull U> ParallelFlowable compose(@NonNull ParallelTransformer composer) { + return RxJavaPlugins.onAssembly(Objects.requireNonNull(composer, "composer is null").apply(this)); } /** - * Generates and flattens Publishers on each 'rail'. + * Generates and flattens {@link Publisher}s on each 'rail'. *

- * Errors are not delayed and uses unbounded concurrency along with default inner prefetch. + * The errors are not delayed and uses unbounded concurrency along with default inner prefetch. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests {@link Flowable#bufferSize()} amount from each rail upfront + * and keeps requesting as many items per rail as many inner sources on + * that rail completed. The inner sources are requested {@link Flowable#bufferSize()} + * amount upfront, then 75% of this amount requested after 75% received.
+ *
Scheduler:
+ *
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher - * @return the new ParallelFlowable instance + * @param mapper the function to map each rail's value into a {@code Publisher} + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable flatMap(@NonNull Function> mapper) { - return flatMap(mapper, false, Integer.MAX_VALUE, Flowable.bufferSize()); + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable flatMap(@NonNull Function> mapper) { + return flatMap(mapper, false, Flowable.bufferSize(), Flowable.bufferSize()); } /** - * Generates and flattens Publishers on each 'rail', optionally delaying errors. + * Generates and flattens {@link Publisher}s on each 'rail', optionally delaying errors. *

* It uses unbounded concurrency along with default inner prefetch. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests {@link Flowable#bufferSize()} amount from each rail upfront + * and keeps requesting as many items per rail as many inner sources on + * that rail completed. The inner sources are requested {@link Flowable#bufferSize()} + * amount upfront, then 75% of this amount requested after 75% received. + *
+ *
Scheduler:
+ *
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher + * @param mapper the function to map each rail's value into a {@code Publisher} * @param delayError should the errors from the main and the inner sources delayed till everybody terminates? - * @return the new ParallelFlowable instance + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable flatMap( - @NonNull Function> mapper, boolean delayError) { - return flatMap(mapper, delayError, Integer.MAX_VALUE, Flowable.bufferSize()); + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable flatMap( + @NonNull Function> mapper, boolean delayError) { + return flatMap(mapper, delayError, Flowable.bufferSize(), Flowable.bufferSize()); } /** - * Generates and flattens Publishers on each 'rail', optionally delaying errors - * and having a total number of simultaneous subscriptions to the inner Publishers. + * Generates and flattens {@link Publisher}s on each 'rail', optionally delaying errors + * and having a total number of simultaneous subscriptions to the inner {@code Publisher}s. *

* It uses a default inner prefetch. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests {@code maxConcurrency} amount from each rail upfront + * and keeps requesting as many items per rail as many inner sources on + * that rail completed. The inner sources are requested {@link Flowable#bufferSize()} + * amount upfront, then 75% of this amount requested after 75% received. + *
+ *
Scheduler:
+ *
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher + * @param mapper the function to map each rail's value into a {@code Publisher} * @param delayError should the errors from the main and the inner sources delayed till everybody terminates? - * @param maxConcurrency the maximum number of simultaneous subscriptions to the generated inner Publishers - * @return the new ParallelFlowable instance + * @param maxConcurrency the maximum number of simultaneous subscriptions to the generated inner {@code Publisher}s + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} is non-positive */ @CheckReturnValue @NonNull - public final ParallelFlowable flatMap( - @NonNull Function> mapper, boolean delayError, int maxConcurrency) { + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable flatMap( + @NonNull Function> mapper, boolean delayError, int maxConcurrency) { return flatMap(mapper, delayError, maxConcurrency, Flowable.bufferSize()); } /** - * Generates and flattens Publishers on each 'rail', optionally delaying errors, - * having a total number of simultaneous subscriptions to the inner Publishers - * and using the given prefetch amount for the inner Publishers. + * Generates and flattens {@link Publisher}s on each 'rail', optionally delaying errors, + * having a total number of simultaneous subscriptions to the inner {@code Publisher}s + * and using the given prefetch amount for the inner {@code Publisher}s. + *
+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests {@code maxConcurrency} amount from each rail upfront + * and keeps requesting as many items per rail as many inner sources on + * that rail completed. The inner sources are requested the {@code prefetch} + * amount upfront, then 75% of this amount requested after 75% received. + *
+ *
Scheduler:
+ *
{@code flatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher + * @param mapper the function to map each rail's value into a {@code Publisher} * @param delayError should the errors from the main and the inner sources delayed till everybody terminates? - * @param maxConcurrency the maximum number of simultaneous subscriptions to the generated inner Publishers - * @param prefetch the number of items to prefetch from each inner Publisher - * @return the new ParallelFlowable instance + * @param maxConcurrency the maximum number of simultaneous subscriptions to the generated inner {@code Publisher}s + * @param prefetch the number of items to prefetch from each inner {@code Publisher} + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code maxConcurrency} or {@code prefetch} is non-positive */ @CheckReturnValue @NonNull - public final ParallelFlowable flatMap( - @NonNull Function> mapper, + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable flatMap( + @NonNull Function> mapper, boolean delayError, int maxConcurrency, int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(maxConcurrency, "maxConcurrency"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelFlatMap(this, mapper, delayError, maxConcurrency, prefetch)); + return RxJavaPlugins.onAssembly(new ParallelFlatMap<>(this, mapper, delayError, maxConcurrency, prefetch)); } /** - * Generates and concatenates Publishers on each 'rail', signalling errors immediately + * Generates and concatenates {@link Publisher}s on each 'rail', signalling errors immediately * and generating 2 publishers upfront. + *
+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests 2 from each rail upfront and keeps requesting 1 when the inner source complete. + * Requests for the inner sources are determined by the downstream rails' + * backpressure behavior.
+ *
Scheduler:
+ *
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher - * source and the inner Publishers (immediate, boundary, end) - * @return the new ParallelFlowable instance + * @param mapper the function to map each rail's value into a {@code Publisher} + * source and the inner {@code Publisher}s (immediate, boundary, end) + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable concatMap( - @NonNull Function> mapper) { + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable concatMap( + @NonNull Function> mapper) { return concatMap(mapper, 2); } /** - * Generates and concatenates Publishers on each 'rail', signalling errors immediately - * and using the given prefetch amount for generating Publishers upfront. + * Generates and concatenates {@link Publisher}s on each 'rail', signalling errors immediately + * and using the given prefetch amount for generating {@code Publisher}s upfront. + *
+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests the {@code prefetch} amount from each rail upfront and keeps + * requesting 75% of this amount after 75% received and the inner sources completed. + * Requests for the inner sources are determined by the downstream rails' + * backpressure behavior.
+ *
Scheduler:
+ *
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher - * @param prefetch the number of items to prefetch from each inner Publisher - * source and the inner Publishers (immediate, boundary, end) - * @return the new ParallelFlowable instance + * @param mapper the function to map each rail's value into a {@code Publisher} + * @param prefetch the number of items to prefetch from each inner {@code Publisher} + * source and the inner {@code Publisher}s (immediate, boundary, end) + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive */ @CheckReturnValue @NonNull - public final ParallelFlowable concatMap( - @NonNull Function> mapper, - int prefetch) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable concatMap( + @NonNull Function> mapper, + int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelConcatMap(this, mapper, prefetch, ErrorMode.IMMEDIATE)); + return RxJavaPlugins.onAssembly(new ParallelConcatMap<>(this, mapper, prefetch, ErrorMode.IMMEDIATE)); } /** - * Generates and concatenates Publishers on each 'rail', optionally delaying errors + * Generates and concatenates {@link Publisher}s on each 'rail', optionally delaying errors * and generating 2 publishers upfront. + *
+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests 2 from each rail upfront and keeps requesting 1 when the inner source complete. + * Requests for the inner sources are determined by the downstream rails' + * backpressure behavior.
+ *
Scheduler:
+ *
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher - * @param tillTheEnd if true all errors from the upstream and inner Publishers are delayed - * till all of them terminate, if false, the error is emitted when an inner Publisher terminates. - * source and the inner Publishers (immediate, boundary, end) - * @return the new ParallelFlowable instance + * @param mapper the function to map each rail's value into a {@code Publisher} + * @param tillTheEnd if {@code true}, all errors from the upstream and inner {@code Publisher}s are delayed + * till all of them terminate, if {@code false}, the error is emitted when an inner {@code Publisher} terminates. + * source and the inner {@code Publisher}s (immediate, boundary, end) + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} */ @CheckReturnValue @NonNull - public final ParallelFlowable concatMapDelayError( - @NonNull Function> mapper, + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable concatMapDelayError( + @NonNull Function> mapper, boolean tillTheEnd) { return concatMapDelayError(mapper, 2, tillTheEnd); } /** - * Generates and concatenates Publishers on each 'rail', optionally delaying errors - * and using the given prefetch amount for generating Publishers upfront. + * Generates and concatenates {@link Publisher}s on each 'rail', optionally delaying errors + * and using the given prefetch amount for generating {@code Publisher}s upfront. + *
+ *
Backpressure:
+ *
The operator honors backpressure from the downstream rails and + * requests the {@code prefetch} amount from each rail upfront and keeps + * requesting 75% of this amount after 75% received and the inner sources completed. + * Requests for the inner sources are determined by the downstream rails' + * backpressure behavior.
+ *
Scheduler:
+ *
{@code concatMap} does not operate by default on a particular {@link Scheduler}.
+ *
* * @param the result type - * @param mapper the function to map each rail's value into a Publisher - * @param prefetch the number of items to prefetch from each inner Publisher - * @param tillTheEnd if true all errors from the upstream and inner Publishers are delayed - * till all of them terminate, if false, the error is emitted when an inner Publisher terminates. - * @return the new ParallelFlowable instance + * @param mapper the function to map each rail's value into a {@code Publisher} + * @param prefetch the number of items to prefetch from each inner {@code Publisher} + * @param tillTheEnd if {@code true}, all errors from the upstream and inner {@code Publisher}s are delayed + * till all of them terminate, if {@code false}, the error is emitted when an inner {@code Publisher} terminates. + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive */ @CheckReturnValue @NonNull - public final ParallelFlowable concatMapDelayError( - @NonNull Function> mapper, + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull R> ParallelFlowable concatMapDelayError( + @NonNull Function> mapper, int prefetch, boolean tillTheEnd) { - ObjectHelper.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(mapper, "mapper is null"); ObjectHelper.verifyPositive(prefetch, "prefetch"); - return RxJavaPlugins.onAssembly(new ParallelConcatMap( + return RxJavaPlugins.onAssembly(new ParallelConcatMap<>( this, mapper, prefetch, tillTheEnd ? ErrorMode.END : ErrorMode.BOUNDARY)); } + + /** + * Returns a {@code ParallelFlowable} that merges each item emitted by the source on each rail with the values in an + * {@link Iterable} corresponding to that item that is generated by a selector. + *

+ * + *

+ *
Backpressure:
+ *
The operator honors backpressure from each downstream rail. The source {@code ParallelFlowable}s is + * expected to honor backpressure as well. If the source {@code ParallelFlowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
+ *
Scheduler:
+ *
{@code flatMapIterable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param + * the type of item emitted by the resulting {@code Iterable} + * @param mapper + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * source {@code ParallelFlowable} + * @return the new {@code ParallelFlowable} instance + * @see ReactiveX operators documentation: FlatMap + * @see #flatMapStream(Function) + * @since 3.0.0 + * @throws NullPointerException if {@code mapper} is {@code null} + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull U> ParallelFlowable flatMapIterable(@NonNull Function> mapper) { + return flatMapIterable(mapper, Flowable.bufferSize()); + } + + /** + * Returns a {@code ParallelFlowable} that merges each item emitted by the source {@code ParallelFlowable} with the values in an + * {@link Iterable} corresponding to that item that is generated by a selector. + *

+ * + *

+ *
Backpressure:
+ *
The operator honors backpressure from each downstream rail. The source {@code ParallelFlowable}s is + * expected to honor backpressure as well. If the source {@code ParallelFlowable} violates the rule, the operator will + * signal a {@link MissingBackpressureException}.
+ *
Scheduler:
+ *
{@code flatMapIterable} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param + * the type of item emitted by the resulting {@code Iterable} + * @param mapper + * a function that returns an {@code Iterable} sequence of values for when given an item emitted by the + * source {@code ParallelFlowable} + * @param bufferSize + * the number of elements to prefetch from each upstream rail + * @return the new {@code ParallelFlowable} instance + * @see ReactiveX operators documentation: FlatMap + * @see #flatMapStream(Function, int) + * @since 3.0.0 + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code bufferSize} is non-positive + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull U> ParallelFlowable flatMapIterable(@NonNull Function> mapper, int bufferSize) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return RxJavaPlugins.onAssembly(new ParallelFlatMapIterable<>(this, mapper, bufferSize)); + } + + // ------------------------------------------------------------------------- + // JDK 8 Support + // ------------------------------------------------------------------------- + + /** + * Maps the source values on each 'rail' to an optional and emits its value if any. + *

+ * Note that the same mapper function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the output value type + * @param mapper the mapper function turning Ts into optional of Rs. + * @return the new {@code ParallelFlowable} instance + * @since 3.0.0 + * @throws NullPointerException if {@code mapper} is {@code null} + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public final <@NonNull R> ParallelFlowable mapOptional(@NonNull Function> mapper) { + Objects.requireNonNull(mapper, "mapper is null"); + return RxJavaPlugins.onAssembly(new ParallelMapOptional<>(this, mapper)); + } + + /** + * Maps the source values on each 'rail' to an optional and emits its value if any and + * handles errors based on the given {@link ParallelFailureHandling} enumeration value. + *

+ * Note that the same mapper function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 2.0.8 - experimental + * @param the output value type + * @param mapper the mapper function turning Ts into optional of Rs. + * @param errorHandler the enumeration that defines how to handle errors thrown + * from the mapper function + * @return the new {@code ParallelFlowable} instance + * @since 3.0.0 + * @throws NullPointerException if {@code mapper} or {@code errorHandler} is {@code null} + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public final <@NonNull R> ParallelFlowable mapOptional(@NonNull Function> mapper, @NonNull ParallelFailureHandling errorHandler) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelMapTryOptional<>(this, mapper, errorHandler)); + } + + /** + * Maps the source values on each 'rail' to an optional and emits its value if any and + * handles errors based on the returned value by the handler function. + *

+ * Note that the same mapper function may be called from multiple threads concurrently. + *

+ *
Backpressure:
+ *
The operator is a pass-through for backpressure and the behavior + * is determined by the upstream and downstream rail behaviors.
+ *
Scheduler:
+ *
{@code map} does not operate by default on a particular {@link Scheduler}.
+ *
+ *

History: 2.0.8 - experimental + * @param the output value type + * @param mapper the mapper function turning Ts into optional of Rs. + * @param errorHandler the function called with the current repeat count and + * failure {@link Throwable} and should return one of the {@link ParallelFailureHandling} + * enumeration values to indicate how to proceed. + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} or {@code errorHandler} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @SchedulerSupport(SchedulerSupport.NONE) + @BackpressureSupport(BackpressureKind.PASS_THROUGH) + public final <@NonNull R> ParallelFlowable mapOptional(@NonNull Function> mapper, @NonNull BiFunction errorHandler) { + Objects.requireNonNull(mapper, "mapper is null"); + Objects.requireNonNull(errorHandler, "errorHandler is null"); + return RxJavaPlugins.onAssembly(new ParallelMapTryOptional<>(this, mapper, errorHandler)); + } + + /** + * Maps each upstream item on each rail into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flatMapIterable(Function)}: + *


+     * source.flatMapIterable(v -> createStream(v)::iterator);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed());
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors the downstream backpressure and consumes the inner stream only on demand. The operator + * prefetches {@link Flowable#bufferSize()} items of the upstream (then 75% of it after the 75% received) + * and caches them until they are ready to be mapped into {@code Stream}s + * after the current {@code Stream} has been consumed.
+ *
Scheduler:
+ *
{@code flatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @see #flatMap(Function) + * @see #flatMapIterable(Function) + * @see #flatMapStream(Function, int) + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> ParallelFlowable flatMapStream(@NonNull Function> mapper) { + return flatMapStream(mapper, Flowable.bufferSize()); + } + + /** + * Maps each upstream item of each rail into a {@link Stream} and emits the {@code Stream}'s items to the downstream in a sequential fashion. + *

+ * + *

+ * Due to the blocking and sequential nature of Java {@code Stream}s, the streams are mapped and consumed in a sequential fashion + * without interleaving (unlike a more general {@link #flatMap(Function)}). Therefore, {@code flatMapStream} and + * {@code concatMapStream} are identical operators and are provided as aliases. + *

+ * The operator closes the {@code Stream} upon cancellation and when it terminates. The exceptions raised when + * closing a {@code Stream} are routed to the global error handler ({@link RxJavaPlugins#onError(Throwable)}. + * If a {@code Stream} should not be closed, turn it into an {@link Iterable} and use {@link #flatMapIterable(Function, int)}: + *


+     * source.flatMapIterable(v -> createStream(v)::iterator, 32);
+     * 
+ *

+ * Note that {@code Stream}s can be consumed only once; any subsequent attempt to consume a {@code Stream} + * will result in an {@link IllegalStateException}. + *

+ * Primitive streams are not supported and items have to be boxed manually (e.g., via {@link IntStream#boxed()}): + *


+     * source.flatMapStream(v -> IntStream.rangeClosed(v + 1, v + 10).boxed(), 32);
+     * 
+ *

+ * {@code Stream} does not support concurrent usage so creating and/or consuming the same instance multiple times + * from multiple threads can lead to undefined behavior. + *

+ *
Backpressure:
+ *
The operator honors the downstream backpressure and consumes the inner stream only on demand. The operator + * prefetches the given amount of upstream items and caches them until they are ready to be mapped into {@code Stream}s + * after the current {@code Stream} has been consumed.
+ *
Scheduler:
+ *
{@code flatMapStream} does not operate by default on a particular {@link Scheduler}.
+ *
+ * + * @param the element type of the {@code Stream}s and the result + * @param mapper the function that receives an upstream item and should return a {@code Stream} whose elements + * will be emitted to the downstream + * @param prefetch the number of upstream items to request upfront, then 75% of this amount after each 75% upstream items received + * @return the new {@code ParallelFlowable} instance + * @throws NullPointerException if {@code mapper} is {@code null} + * @throws IllegalArgumentException if {@code prefetch} is non-positive + * @see #flatMap(Function, boolean, int) + * @see #flatMapIterable(Function, int) + * @since 3.0.0 + */ + @CheckReturnValue + @BackpressureSupport(BackpressureKind.FULL) + @SchedulerSupport(SchedulerSupport.NONE) + @NonNull + public final <@NonNull R> ParallelFlowable flatMapStream(@NonNull Function> mapper, int prefetch) { + Objects.requireNonNull(mapper, "mapper is null"); + ObjectHelper.verifyPositive(prefetch, "prefetch"); + return RxJavaPlugins.onAssembly(new ParallelFlatMapStream<>(this, mapper, prefetch)); + } + + /** + * Reduces all values within a 'rail' and across 'rails' with a callbacks + * of the given {@link Collector} into one {@link Flowable} containing a single value. + *

+ * Each parallel rail receives its own {@link Collector#accumulator()} and + * {@link Collector#combiner()}. + *

+ *
Backpressure:
+ *
The operator honors backpressure from the downstream and consumes + * the upstream rails in an unbounded manner (requesting {@link Long#MAX_VALUE}).
+ *
Scheduler:
+ *
{@code collect} does not operate by default on a particular {@link Scheduler}.
+ *
+ * @param the accumulator type + * @param the output value type + * @param collector the {@code Collector} instance + * @return the new {@code Flowable} instance emitting the collected value. + * @throws NullPointerException if {@code collector} is {@code null} + * @since 3.0.0 + */ + @CheckReturnValue + @NonNull + @BackpressureSupport(BackpressureKind.UNBOUNDED_IN) + @SchedulerSupport(SchedulerSupport.NONE) + public final <@NonNull A, @NonNull R> Flowable collect(@NonNull Collector collector) { + Objects.requireNonNull(collector, "collector is null"); + return RxJavaPlugins.onAssembly(new ParallelCollector<>(this, collector)); + } } diff --git a/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowableConverter.java b/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowableConverter.java index ea2d07502a..47510faf37 100644 --- a/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowableConverter.java +++ b/src/main/java/io/reactivex/rxjava3/parallel/ParallelFlowableConverter.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,6 +23,7 @@ * @param the output type * @since 2.2 */ +@FunctionalInterface public interface ParallelFlowableConverter { /** * Applies a function to the upstream ParallelFlowable and returns a converted value of type {@code R}. diff --git a/src/main/java/io/reactivex/rxjava3/parallel/ParallelTransformer.java b/src/main/java/io/reactivex/rxjava3/parallel/ParallelTransformer.java index 1a918f0835..f35f71567c 100644 --- a/src/main/java/io/reactivex/rxjava3/parallel/ParallelTransformer.java +++ b/src/main/java/io/reactivex/rxjava3/parallel/ParallelTransformer.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,8 @@ * @param the downstream value type * @since 2.2 */ -public interface ParallelTransformer { +@FunctionalInterface +public interface ParallelTransformer<@NonNull Upstream, @NonNull Downstream> { /** * Applies a function to the upstream ParallelFlowable and returns a ParallelFlowable with * optionally different element type. @@ -31,4 +32,4 @@ public interface ParallelTransformer { */ @NonNull ParallelFlowable apply(@NonNull ParallelFlowable upstream); -} \ No newline at end of file +} diff --git a/src/main/java/io/reactivex/rxjava3/parallel/package-info.java b/src/main/java/io/reactivex/rxjava3/parallel/package-info.java index 7c9cc785ad..09605021f0 100644 --- a/src/main/java/io/reactivex/rxjava3/parallel/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/parallel/package-info.java @@ -1,21 +1,18 @@ -/** +/* * 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. */ /** * Contains the base type {@link io.reactivex.rxjava3.parallel.ParallelFlowable}, * a sub-DSL for working with {@link io.reactivex.rxjava3.core.Flowable} sequences in parallel. */ -package io.reactivex.rxjava3.parallel; \ No newline at end of file +package io.reactivex.rxjava3.parallel; diff --git a/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java b/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java index 976289c542..2949253b31 100644 --- a/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java +++ b/src/main/java/io/reactivex/rxjava3/plugins/RxJavaPlugins.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,10 +10,12 @@ * 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 io.reactivex.rxjava3.plugins; import java.lang.Thread.UncaughtExceptionHandler; -import java.util.concurrent.ThreadFactory; +import java.util.Objects; +import java.util.concurrent.*; import org.reactivestreams.Subscriber; @@ -22,7 +24,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.flowables.ConnectableFlowable; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.schedulers.*; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.observables.ConnectableObservable; @@ -95,22 +96,26 @@ public final class RxJavaPlugins { @SuppressWarnings("rawtypes") @Nullable - static volatile BiFunction onFlowableSubscribe; + static volatile BiFunction onFlowableSubscribe; @SuppressWarnings("rawtypes") @Nullable - static volatile BiFunction onMaybeSubscribe; + static volatile BiFunction onMaybeSubscribe; @SuppressWarnings("rawtypes") @Nullable - static volatile BiFunction onObservableSubscribe; + static volatile BiFunction onObservableSubscribe; @SuppressWarnings("rawtypes") @Nullable - static volatile BiFunction onSingleSubscribe; + static volatile BiFunction onSingleSubscribe; + + @Nullable + static volatile BiFunction onCompletableSubscribe; + @SuppressWarnings("rawtypes") @Nullable - static volatile BiFunction onCompletableSubscribe; + static volatile BiFunction onParallelSubscribe; @Nullable static volatile BooleanSupplier onBeforeBlocking; @@ -266,7 +271,7 @@ public static Consumer getErrorHandler() { */ @NonNull public static Scheduler initComputationScheduler(@NonNull Supplier defaultScheduler) { - ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); + Objects.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); Function, ? extends Scheduler> f = onInitComputationHandler; if (f == null) { return callRequireNonNull(defaultScheduler); @@ -282,7 +287,7 @@ public static Scheduler initComputationScheduler(@NonNull Supplier de */ @NonNull public static Scheduler initIoScheduler(@NonNull Supplier defaultScheduler) { - ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); + Objects.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); Function, ? extends Scheduler> f = onInitIoHandler; if (f == null) { return callRequireNonNull(defaultScheduler); @@ -298,7 +303,7 @@ public static Scheduler initIoScheduler(@NonNull Supplier defaultSche */ @NonNull public static Scheduler initNewThreadScheduler(@NonNull Supplier defaultScheduler) { - ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); + Objects.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); Function, ? extends Scheduler> f = onInitNewThreadHandler; if (f == null) { return callRequireNonNull(defaultScheduler); @@ -314,7 +319,7 @@ public static Scheduler initNewThreadScheduler(@NonNull Supplier defa */ @NonNull public static Scheduler initSingleScheduler(@NonNull Supplier defaultScheduler) { - ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); + Objects.requireNonNull(defaultScheduler, "Scheduler Supplier can't be null"); Function, ? extends Scheduler> f = onInitSingleHandler; if (f == null) { return callRequireNonNull(defaultScheduler); @@ -397,10 +402,13 @@ static boolean isBug(Throwable error) { return true; } // the sender didn't honor the request amount - // it's either due to an operator bug or concurrent onNext if (error instanceof MissingBackpressureException) { return true; } + // it's either due to an operator bug or concurrent onNext + if (error instanceof QueueOverflowException) { + return true; + } // general protocol violations // it's either due to an operator bug or concurrent onNext if (error instanceof IllegalStateException) { @@ -464,7 +472,7 @@ public static Scheduler onNewThreadScheduler(@NonNull Scheduler defaultScheduler */ @NonNull public static Runnable onSchedule(@NonNull Runnable run) { - ObjectHelper.requireNonNull(run, "run is null"); + Objects.requireNonNull(run, "run is null"); Function f = onScheduleHandler; if (f == null) { @@ -525,6 +533,7 @@ public static void reset() { setOnMaybeSubscribe(null); setOnParallelAssembly(null); + setOnParallelSubscribe(null); setFailOnNonBlockingScheduler(false); setOnBeforeBlocking(null); @@ -661,7 +670,7 @@ public static void setSingleSchedulerHandler(@Nullable Function getOnCompletableSubscribe() { + public static BiFunction getOnCompletableSubscribe() { return onCompletableSubscribe; } @@ -691,7 +700,7 @@ public static void setSingleSchedulerHandler(@Nullable Function getOnFlowableSubscribe() { + public static BiFunction getOnFlowableSubscribe() { return onFlowableSubscribe; } @@ -701,7 +710,7 @@ public static void setSingleSchedulerHandler(@Nullable Function getOnMaybeSubscribe() { + public static BiFunction getOnMaybeSubscribe() { return onMaybeSubscribe; } @@ -731,7 +740,7 @@ public static void setSingleSchedulerHandler(@Nullable Function getOnSingleSubscribe() { + public static BiFunction getOnSingleSubscribe() { return onSingleSubscribe; } @@ -761,7 +770,7 @@ public static void setSingleSchedulerHandler(@Nullable Function getOnObservableSubscribe() { + public static BiFunction getOnObservableSubscribe() { return onObservableSubscribe; } @@ -781,7 +790,7 @@ public static void setOnCompletableAssembly(@Nullable Function onCompletableSubscribe) { + @Nullable BiFunction onCompletableSubscribe) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); } @@ -829,7 +838,7 @@ public static void setOnConnectableFlowableAssembly(@Nullable Function onFlowableSubscribe) { + public static void setOnFlowableSubscribe(@Nullable BiFunction onFlowableSubscribe) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); } @@ -841,7 +850,7 @@ public static void setOnFlowableSubscribe(@Nullable BiFunction onMaybeSubscribe) { + public static void setOnMaybeSubscribe(@Nullable BiFunction onMaybeSubscribe) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); } @@ -878,7 +887,7 @@ public static void setOnConnectableObservableAssembly(@Nullable Function onObservableSubscribe) { + @Nullable BiFunction onObservableSubscribe) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); } @@ -902,7 +911,7 @@ public static void setOnSingleAssembly(@Nullable Function onSingleSubscribe) { + public static void setOnSingleSubscribe(@Nullable BiFunction onSingleSubscribe) { if (lockdown) { throw new IllegalStateException("Plugins can't be changed anymore"); } @@ -918,8 +927,8 @@ public static void setOnSingleSubscribe(@Nullable BiFunction Subscriber onSubscribe(@NonNull Flowable source, @NonNull Subscriber subscriber) { - BiFunction f = onFlowableSubscribe; + public static <@NonNull T> Subscriber onSubscribe(@NonNull Flowable source, @NonNull Subscriber subscriber) { + BiFunction f = onFlowableSubscribe; if (f != null) { return apply(f, source, subscriber); } @@ -935,8 +944,8 @@ public static Subscriber onSubscribe(@NonNull Flowable source, */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static Observer onSubscribe(@NonNull Observable source, @NonNull Observer observer) { - BiFunction f = onObservableSubscribe; + public static <@NonNull T> Observer onSubscribe(@NonNull Observable source, @NonNull Observer observer) { + BiFunction f = onObservableSubscribe; if (f != null) { return apply(f, source, observer); } @@ -952,8 +961,8 @@ public static Observer onSubscribe(@NonNull Observable source, */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static SingleObserver onSubscribe(@NonNull Single source, @NonNull SingleObserver observer) { - BiFunction f = onSingleSubscribe; + public static <@NonNull T> SingleObserver onSubscribe(@NonNull Single source, @NonNull SingleObserver observer) { + BiFunction f = onSingleSubscribe; if (f != null) { return apply(f, source, observer); } @@ -968,7 +977,7 @@ public static SingleObserver onSubscribe(@NonNull Single sourc */ @NonNull public static CompletableObserver onSubscribe(@NonNull Completable source, @NonNull CompletableObserver observer) { - BiFunction f = onCompletableSubscribe; + BiFunction f = onCompletableSubscribe; if (f != null) { return apply(f, source, observer); } @@ -984,14 +993,31 @@ public static CompletableObserver onSubscribe(@NonNull Completable source, @NonN */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static MaybeObserver onSubscribe(@NonNull Maybe source, @NonNull MaybeObserver observer) { - BiFunction f = onMaybeSubscribe; + public static <@NonNull T> MaybeObserver onSubscribe(@NonNull Maybe source, @NonNull MaybeObserver observer) { + BiFunction f = onMaybeSubscribe; if (f != null) { return apply(f, source, observer); } return observer; } + /** + * Calls the associated hook function. + * @param the value type + * @param source the hook's input value + * @param subscribers the array of subscribers + * @return the value returned by the hook + */ + @SuppressWarnings({ "rawtypes" }) + @NonNull + public static <@NonNull T> Subscriber[] onSubscribe(@NonNull ParallelFlowable source, @NonNull Subscriber[] subscribers) { + BiFunction f = onParallelSubscribe; + if (f != null) { + return apply(f, source, subscribers); + } + return subscribers; + } + /** * Calls the associated hook function. * @param the value type @@ -1000,7 +1026,7 @@ public static MaybeObserver onSubscribe(@NonNull Maybe source, */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static Maybe onAssembly(@NonNull Maybe source) { + public static <@NonNull T> Maybe onAssembly(@NonNull Maybe source) { Function f = onMaybeAssembly; if (f != null) { return apply(f, source); @@ -1016,7 +1042,7 @@ public static Maybe onAssembly(@NonNull Maybe source) { */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static Flowable onAssembly(@NonNull Flowable source) { + public static <@NonNull T> Flowable onAssembly(@NonNull Flowable source) { Function f = onFlowableAssembly; if (f != null) { return apply(f, source); @@ -1032,7 +1058,7 @@ public static Flowable onAssembly(@NonNull Flowable source) { */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static ConnectableFlowable onAssembly(@NonNull ConnectableFlowable source) { + public static <@NonNull T> ConnectableFlowable onAssembly(@NonNull ConnectableFlowable source) { Function f = onConnectableFlowableAssembly; if (f != null) { return apply(f, source); @@ -1048,7 +1074,7 @@ public static ConnectableFlowable onAssembly(@NonNull ConnectableFlowable */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static Observable onAssembly(@NonNull Observable source) { + public static <@NonNull T> Observable onAssembly(@NonNull Observable source) { Function f = onObservableAssembly; if (f != null) { return apply(f, source); @@ -1064,7 +1090,7 @@ public static Observable onAssembly(@NonNull Observable source) { */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static ConnectableObservable onAssembly(@NonNull ConnectableObservable source) { + public static <@NonNull T> ConnectableObservable onAssembly(@NonNull ConnectableObservable source) { Function f = onConnectableObservableAssembly; if (f != null) { return apply(f, source); @@ -1080,7 +1106,7 @@ public static ConnectableObservable onAssembly(@NonNull ConnectableObserv */ @SuppressWarnings({ "rawtypes", "unchecked" }) @NonNull - public static Single onAssembly(@NonNull Single source) { + public static <@NonNull T> Single onAssembly(@NonNull Single source) { Function f = onSingleAssembly; if (f != null) { return apply(f, source); @@ -1128,6 +1154,32 @@ public static void setOnParallelAssembly(@Nullable FunctionHistory: 3.0.11 - experimental + * @param handler the hook function to set, null allowed + * @since 3.1.0 + */ + @SuppressWarnings("rawtypes") + public static void setOnParallelSubscribe(@Nullable BiFunction handler) { + if (lockdown) { + throw new IllegalStateException("Plugins can't be changed anymore"); + } + onParallelSubscribe = handler; + } + + /** + * Returns the current hook function. + *

History: 3.0.11 - experimental + * @return the hook function, may be null + * @since 3.1.0 + */ + @SuppressWarnings("rawtypes") + @Nullable + public static BiFunction getOnParallelSubscribe() { + return onParallelSubscribe; + } + /** * Calls the associated hook function. *

History: 2.0.6 - experimental; 2.1 - beta @@ -1138,7 +1190,7 @@ public static void setOnParallelAssembly(@Nullable Function ParallelFlowable onAssembly(@NonNull ParallelFlowable source) { + public static <@NonNull T> ParallelFlowable onAssembly(@NonNull ParallelFlowable source) { Function f = onParallelAssembly; if (f != null) { return apply(f, source); @@ -1208,7 +1260,7 @@ public static BooleanSupplier getOnBeforeBlocking() { */ @NonNull public static Scheduler createComputationScheduler(@NonNull ThreadFactory threadFactory) { - return new ComputationScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null")); + return new ComputationScheduler(Objects.requireNonNull(threadFactory, "threadFactory is null")); } /** @@ -1222,7 +1274,7 @@ public static Scheduler createComputationScheduler(@NonNull ThreadFactory thread */ @NonNull public static Scheduler createIoScheduler(@NonNull ThreadFactory threadFactory) { - return new IoScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null")); + return new IoScheduler(Objects.requireNonNull(threadFactory, "threadFactory is null")); } /** @@ -1236,7 +1288,7 @@ public static Scheduler createIoScheduler(@NonNull ThreadFactory threadFactory) */ @NonNull public static Scheduler createNewThreadScheduler(@NonNull ThreadFactory threadFactory) { - return new NewThreadScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null")); + return new NewThreadScheduler(Objects.requireNonNull(threadFactory, "threadFactory is null")); } /** @@ -1250,7 +1302,27 @@ public static Scheduler createNewThreadScheduler(@NonNull ThreadFactory threadFa */ @NonNull public static Scheduler createSingleScheduler(@NonNull ThreadFactory threadFactory) { - return new SingleScheduler(ObjectHelper.requireNonNull(threadFactory, "threadFactory is null")); + return new SingleScheduler(Objects.requireNonNull(threadFactory, "threadFactory is null")); + } + + /** + * Create an instance of a {@link Scheduler} by wrapping an existing {@link Executor}. + *

+ * This method allows creating an {@code Executor}-backed {@code Scheduler} before the {@link Schedulers} class + * would initialize the standard {@code Scheduler}s. + * + * @param executor the {@code Executor} to wrap and turn into a {@code Scheduler}. + * @param interruptibleWorker if {@code true}, the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will + * be interrupted when the task is disposed. + * @param fair if {@code true}, tasks submitted to the {@code Scheduler} or {@code Worker} will be executed by the underlying {@code Executor} one after the other, still + * in a FIFO and non-overlapping manner, but allows interleaving with other tasks submitted to the underlying {@code Executor}. + * If {@code false}, the underlying FIFO scheme will execute as many tasks as it can before giving up the underlying {@code Executor} thread. + * @return the new {@code Scheduler} wrapping the {@code Executor} + * @since 3.1.0 + */ + @NonNull + public static Scheduler createExecutorScheduler(@NonNull Executor executor, boolean interruptibleWorker, boolean fair) { + return new ExecutorScheduler(executor, interruptibleWorker, fair); } /** @@ -1263,7 +1335,7 @@ public static Scheduler createSingleScheduler(@NonNull ThreadFactory threadFacto * @return the result of the function call */ @NonNull - static R apply(@NonNull Function f, @NonNull T t) { + static <@NonNull T, @NonNull R> R apply(@NonNull Function f, @NonNull T t) { try { return f.apply(t); } catch (Throwable ex) { @@ -1283,7 +1355,7 @@ static R apply(@NonNull Function f, @NonNull T t) { * @return the result of the function call */ @NonNull - static R apply(@NonNull BiFunction f, @NonNull T t, @NonNull U u) { + static <@NonNull T, @NonNull U, @NonNull R> R apply(@NonNull BiFunction f, @NonNull T t, @NonNull U u) { try { return f.apply(t, u); } catch (Throwable ex) { @@ -1301,7 +1373,7 @@ static R apply(@NonNull BiFunction f, @NonNull T t, @NonNull @NonNull static Scheduler callRequireNonNull(@NonNull Supplier s) { try { - return ObjectHelper.requireNonNull(s.get(), "Scheduler Supplier result can't be null"); + return Objects.requireNonNull(s.get(), "Scheduler Supplier result can't be null"); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -1317,7 +1389,7 @@ static Scheduler callRequireNonNull(@NonNull Supplier s) { */ @NonNull static Scheduler applyRequireNonNull(@NonNull Function, ? extends Scheduler> f, Supplier s) { - return ObjectHelper.requireNonNull(apply(f, s), "Scheduler Supplier result can't be null"); + return Objects.requireNonNull(apply(f, s), "Scheduler Supplier result can't be null"); } /** Helper class, no instances. */ diff --git a/src/main/java/io/reactivex/rxjava3/plugins/package-info.java b/src/main/java/io/reactivex/rxjava3/plugins/package-info.java index a3105506a4..49390584df 100644 --- a/src/main/java/io/reactivex/rxjava3/plugins/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/plugins/package-info.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ /** diff --git a/src/main/java/io/reactivex/rxjava3/processors/AsyncProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/AsyncProcessor.java index 9a75a1f4ca..b20c8b23ae 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/AsyncProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/AsyncProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.processors; import java.util.concurrent.atomic.AtomicReference; @@ -68,7 +69,7 @@ *

The {@code AsyncProcessor} honors the backpressure of the downstream {@code Subscriber}s and won't emit * its single value to a particular {@code Subscriber} until that {@code Subscriber} has requested an item. * When the {@code AsyncProcessor} is subscribed to a {@link io.reactivex.rxjava3.core.Flowable}, the processor consumes this - * {@code Flowable} in an unbounded manner (requesting `Long.MAX_VALUE`) as only the very last upstream item is + * {@code Flowable} in an unbounded manner (requesting {@link Long#MAX_VALUE}) as only the very last upstream item is * retained by it. *
*
Scheduler:
@@ -113,7 +114,7 @@ * * @param the value type */ -public final class AsyncProcessor extends FlowableProcessor { +public final class AsyncProcessor<@NonNull T> extends FlowableProcessor { @SuppressWarnings("rawtypes") static final AsyncSubscription[] EMPTY = new AsyncSubscription[0]; @@ -137,7 +138,7 @@ public final class AsyncProcessor extends FlowableProcessor { @CheckReturnValue @NonNull public static AsyncProcessor create() { - return new AsyncProcessor(); + return new AsyncProcessor<>(); } /** @@ -146,11 +147,11 @@ public static AsyncProcessor create() { */ @SuppressWarnings("unchecked") AsyncProcessor() { - this.subscribers = new AtomicReference[]>(EMPTY); + this.subscribers = new AtomicReference<>(EMPTY); } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { if (subscribers.get() == TERMINATED) { s.cancel(); return; @@ -160,7 +161,7 @@ public void onSubscribe(Subscription s) { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { ExceptionHelper.nullCheck(t, "onNext called with a null value."); if (subscribers.get() == TERMINATED) { return; @@ -170,7 +171,7 @@ public void onNext(T t) { @SuppressWarnings("unchecked") @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { ExceptionHelper.nullCheck(t, "onError called with a null Throwable."); if (subscribers.get() == TERMINATED) { RxJavaPlugins.onError(t); @@ -203,29 +204,33 @@ public void onComplete() { } @Override + @CheckReturnValue public boolean hasSubscribers() { return subscribers.get().length != 0; } @Override + @CheckReturnValue public boolean hasThrowable() { return subscribers.get() == TERMINATED && error != null; } @Override + @CheckReturnValue public boolean hasComplete() { return subscribers.get() == TERMINATED && error == null; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { return subscribers.get() == TERMINATED ? error : null; } @Override - protected void subscribeActual(Subscriber s) { - AsyncSubscription as = new AsyncSubscription(s, this); + protected void subscribeActual(@NonNull Subscriber s) { + AsyncSubscription as = new AsyncSubscription<>(s, this); s.onSubscribe(as); if (add(as)) { if (as.isCancelled()) { @@ -316,6 +321,7 @@ void remove(AsyncSubscription ps) { *

The method is thread-safe. * @return true if this processor has any value */ + @CheckReturnValue public boolean hasValue() { return subscribers.get() == TERMINATED && value != null; } @@ -326,11 +332,12 @@ public boolean hasValue() { * @return a single value this processor currently has or null if no such value exists */ @Nullable + @CheckReturnValue public T getValue() { return subscribers.get() == TERMINATED ? value : null; } - static final class AsyncSubscription extends DeferredScalarSubscription { + static final class AsyncSubscription<@NonNull T> extends DeferredScalarSubscription { private static final long serialVersionUID = 5629876084736248016L; final AsyncProcessor parent; diff --git a/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java index 5ac1d1d607..2e5117ca56 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/BehaviorProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.processors; +import java.util.Objects; import java.util.concurrent.atomic.*; import java.util.concurrent.locks.*; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.internal.util.AppendOnlyLinkedArrayList.NonThrowingPredicate; @@ -108,7 +108,7 @@ * that returns true if any of the {@code Subscriber}s is not ready to receive {@code onNext} events. If * there are no {@code Subscriber}s to the processor, {@code offer()} always succeeds. * If the {@code BehaviorProcessor} is (optionally) subscribed to another {@code Publisher}, this upstream - * {@code Publisher} is consumed in an unbounded fashion (requesting {@code Long.MAX_VALUE}). + * {@code Publisher} is consumed in an unbounded fashion (requesting {@link Long#MAX_VALUE}). *

Scheduler:
*
{@code BehaviorProcessor} does not operate by default on a particular {@link io.reactivex.rxjava3.core.Scheduler} and * the {@code Subscriber}s get notified on the thread the respective {@code onXXX} methods were invoked.
@@ -160,7 +160,7 @@ * @param * the type of item expected to be observed and emitted by the Processor */ -public final class BehaviorProcessor extends FlowableProcessor { +public final class BehaviorProcessor<@NonNull T> extends FlowableProcessor { final AtomicReference[]> subscribers; static final Object[] EMPTY_ARRAY = new Object[0]; @@ -191,7 +191,7 @@ public final class BehaviorProcessor extends FlowableProcessor { @CheckReturnValue @NonNull public static BehaviorProcessor create() { - return new BehaviorProcessor(); + return new BehaviorProcessor<>(); } /** @@ -204,12 +204,13 @@ public static BehaviorProcessor create() { * the item that will be emitted first to any {@link Subscriber} as long as the * {@link BehaviorProcessor} has not yet observed any items from its source {@code Observable} * @return the constructed {@link BehaviorProcessor} + * @throws NullPointerException if {@code defaultValue} is {@code null} */ @CheckReturnValue @NonNull - public static BehaviorProcessor createDefault(T defaultValue) { - ObjectHelper.requireNonNull(defaultValue, "defaultValue is null"); - return new BehaviorProcessor(defaultValue); + public static <@NonNull T> BehaviorProcessor createDefault(T defaultValue) { + Objects.requireNonNull(defaultValue, "defaultValue is null"); + return new BehaviorProcessor<>(defaultValue); } /** @@ -218,28 +219,28 @@ public static BehaviorProcessor createDefault(T defaultValue) { */ @SuppressWarnings("unchecked") BehaviorProcessor() { - this.value = new AtomicReference(); + this.value = new AtomicReference<>(); this.lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); - this.subscribers = new AtomicReference[]>(EMPTY); - this.terminalEvent = new AtomicReference(); + this.subscribers = new AtomicReference<>(EMPTY); + this.terminalEvent = new AtomicReference<>(); } /** * Constructs a BehaviorProcessor with the given initial value. * @param defaultValue the initial value, not null (verified) - * @throws NullPointerException if {@code defaultValue} is null + * @throws NullPointerException if {@code defaultValue} is {@code null} * @since 2.0 */ BehaviorProcessor(T defaultValue) { this(); - this.value.lazySet(ObjectHelper.requireNonNull(defaultValue, "defaultValue is null")); + this.value.lazySet(defaultValue); } @Override - protected void subscribeActual(Subscriber s) { - BehaviorSubscription bs = new BehaviorSubscription(s, this); + protected void subscribeActual(@NonNull Subscriber s) { + BehaviorSubscription bs = new BehaviorSubscription<>(s, this); s.onSubscribe(bs); if (add(bs)) { if (bs.cancelled) { @@ -258,7 +259,7 @@ protected void subscribeActual(Subscriber s) { } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { if (terminalEvent.get() != null) { s.cancel(); return; @@ -267,7 +268,7 @@ public void onSubscribe(Subscription s) { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { ExceptionHelper.nullCheck(t, "onNext called with a null value."); if (terminalEvent.get() != null) { @@ -281,7 +282,7 @@ public void onNext(T t) { } @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { ExceptionHelper.nullCheck(t, "onError called with a null Throwable."); if (!terminalEvent.compareAndSet(null, t)) { RxJavaPlugins.onError(t); @@ -305,24 +306,21 @@ public void onComplete() { } /** - * Tries to emit the item to all currently subscribed Subscribers if all of them - * has requested some value, returns false otherwise. - *

- * This method should be called in a sequential manner just like the onXXX methods - * of the PublishProcessor. + * Tries to emit the item to all currently subscribed {@link Subscriber}s if all of them + * has requested some value, returns {@code false} otherwise. *

- * Calling with a null value will terminate the PublishProcessor and a NullPointerException - * is signaled to the Subscribers. + * This method should be called in a sequential manner just like the {@code onXXX} methods + * of this {@code BehaviorProcessor}. *

History: 2.0.8 - experimental - * @param t the item to emit, not null - * @return true if the item was emitted to all Subscribers + * @param t the item to emit, not {@code null} + * @return {@code true} if the item was emitted to all {@code Subscriber}s + * @throws NullPointerException if {@code t} is {@code null} * @since 2.2 */ - public boolean offer(T t) { - if (t == null) { - onError(ExceptionHelper.createNullPointerException("offer called with a null value.")); - return true; - } + @CheckReturnValue + public boolean offer(@NonNull T t) { + ExceptionHelper.nullCheck(t, "offer called with a null value."); + BehaviorSubscription[] array = subscribers.get(); for (BehaviorSubscription s : array) { @@ -340,16 +338,19 @@ public boolean offer(T t) { } @Override + @CheckReturnValue public boolean hasSubscribers() { return subscribers.get().length != 0; } + @CheckReturnValue /* test support*/ int subscriberCount() { return subscribers.get().length; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { Object o = value.get(); if (NotificationLite.isError(o)) { @@ -364,6 +365,7 @@ public Throwable getThrowable() { * @return a single value the BehaviorProcessor currently has or null if no such value exists */ @Nullable + @CheckReturnValue public T getValue() { Object o = value.get(); if (NotificationLite.isComplete(o) || NotificationLite.isError(o)) { @@ -373,12 +375,14 @@ public T getValue() { } @Override + @CheckReturnValue public boolean hasComplete() { Object o = value.get(); return NotificationLite.isComplete(o); } @Override + @CheckReturnValue public boolean hasThrowable() { Object o = value.get(); return NotificationLite.isError(o); @@ -389,6 +393,7 @@ public boolean hasThrowable() { *

The method is thread-safe. * @return true if the BehaviorProcessor has any value */ + @CheckReturnValue public boolean hasValue() { Object o = value.get(); return o != null && !NotificationLite.isComplete(o) && !NotificationLite.isError(o); @@ -447,16 +452,9 @@ void remove(BehaviorSubscription rs) { @SuppressWarnings("unchecked") BehaviorSubscription[] terminate(Object terminalValue) { - BehaviorSubscription[] a = subscribers.get(); - if (a != TERMINATED) { - a = subscribers.getAndSet(TERMINATED); - if (a != TERMINATED) { - // either this or atomics with lots of allocation - setCurrent(terminalValue); - } - } + setCurrent(terminalValue); - return a; + return subscribers.getAndSet(TERMINATED); } void setCurrent(Object o) { @@ -467,7 +465,7 @@ void setCurrent(Object o) { wl.unlock(); } - static final class BehaviorSubscription extends AtomicLong implements Subscription, NonThrowingPredicate { + static final class BehaviorSubscription<@NonNull T> extends AtomicLong implements Subscription, NonThrowingPredicate { private static final long serialVersionUID = 3293175281126227086L; @@ -554,7 +552,7 @@ void emitNext(Object value, long stateIndex) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(value); @@ -592,7 +590,7 @@ public boolean test(Object o) { return false; } cancel(); - downstream.onError(new MissingBackpressureException("Could not deliver value due to lack of requests")); + downstream.onError(MissingBackpressureException.createDefault()); return true; } diff --git a/src/main/java/io/reactivex/rxjava3/processors/FlowableProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/FlowableProcessor.java index abe5e2ad08..2bfbb33bd0 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/FlowableProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/FlowableProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,13 +26,14 @@ * * @param the item value type */ -public abstract class FlowableProcessor extends Flowable implements Processor, FlowableSubscriber { +public abstract class FlowableProcessor<@NonNull T> extends Flowable implements Processor, FlowableSubscriber { /** * Returns true if the FlowableProcessor has subscribers. *

The method is thread-safe. * @return true if the FlowableProcessor has subscribers */ + @CheckReturnValue public abstract boolean hasSubscribers(); /** @@ -42,6 +43,7 @@ public abstract class FlowableProcessor extends Flowable implements Proces * @see #getThrowable() * @see #hasComplete() */ + @CheckReturnValue public abstract boolean hasThrowable(); /** @@ -50,6 +52,7 @@ public abstract class FlowableProcessor extends Flowable implements Proces * @return true if the FlowableProcessor has reached a terminal state through a complete event * @see #hasThrowable() */ + @CheckReturnValue public abstract boolean hasComplete(); /** @@ -60,6 +63,7 @@ public abstract class FlowableProcessor extends Flowable implements Proces * hasn't terminated yet */ @Nullable + @CheckReturnValue public abstract Throwable getThrowable(); /** @@ -74,6 +78,6 @@ public final FlowableProcessor toSerialized() { if (this instanceof SerializedProcessor) { return this; } - return new SerializedProcessor(this); + return new SerializedProcessor<>(this); } } diff --git a/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java index 50497473ac..14a7a55ee2 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/MulticastProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,10 +20,9 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; -import io.reactivex.rxjava3.internal.queue.*; import io.reactivex.rxjava3.internal.subscriptions.*; -import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -130,7 +129,7 @@ */ @BackpressureSupport(BackpressureKind.FULL) @SchedulerSupport(SchedulerSupport.NONE) -public final class MulticastProcessor extends FlowableProcessor { +public final class MulticastProcessor<@NonNull T> extends FlowableProcessor { final AtomicInteger wip; @@ -138,8 +137,6 @@ public final class MulticastProcessor extends FlowableProcessor { final AtomicReference[]> subscribers; - final AtomicBoolean once; - final int bufferSize; final int limit; @@ -170,7 +167,7 @@ public final class MulticastProcessor extends FlowableProcessor { @CheckReturnValue @NonNull public static MulticastProcessor create() { - return new MulticastProcessor(bufferSize(), false); + return new MulticastProcessor<>(bufferSize(), false); } /** @@ -184,7 +181,7 @@ public static MulticastProcessor create() { @CheckReturnValue @NonNull public static MulticastProcessor create(boolean refCount) { - return new MulticastProcessor(bufferSize(), refCount); + return new MulticastProcessor<>(bufferSize(), refCount); } /** @@ -192,11 +189,13 @@ public static MulticastProcessor create(boolean refCount) { * @param bufferSize the prefetch amount * @param the input and output value type * @return the new MulticastProcessor instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive */ @CheckReturnValue @NonNull public static MulticastProcessor create(int bufferSize) { - return new MulticastProcessor(bufferSize, false); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return new MulticastProcessor<>(bufferSize, false); } /** @@ -207,11 +206,13 @@ public static MulticastProcessor create(int bufferSize) { * is cancelled * @param the input and output value type * @return the new MulticastProcessor instance + * @throws IllegalArgumentException if {@code bufferSize} is non-positive */ @CheckReturnValue @NonNull public static MulticastProcessor create(int bufferSize, boolean refCount) { - return new MulticastProcessor(bufferSize, refCount); + ObjectHelper.verifyPositive(bufferSize, "bufferSize"); + return new MulticastProcessor<>(bufferSize, refCount); } /** @@ -223,14 +224,12 @@ public static MulticastProcessor create(int bufferSize, boolean refCount) */ @SuppressWarnings("unchecked") MulticastProcessor(int bufferSize, boolean refCount) { - ObjectHelper.verifyPositive(bufferSize, "bufferSize"); this.bufferSize = bufferSize; this.limit = bufferSize - (bufferSize >> 2); this.wip = new AtomicInteger(); - this.subscribers = new AtomicReference[]>(EMPTY); - this.upstream = new AtomicReference(); + this.subscribers = new AtomicReference<>(EMPTY); + this.upstream = new AtomicReference<>(); this.refcount = refCount; - this.once = new AtomicBoolean(); } /** @@ -241,7 +240,7 @@ public static MulticastProcessor create(int bufferSize, boolean refCount) */ public void start() { if (SubscriptionHelper.setOnce(upstream, EmptySubscription.INSTANCE)) { - queue = new SpscArrayQueue(bufferSize); + queue = new SpscArrayQueue<>(bufferSize); } } @@ -253,12 +252,12 @@ public void start() { */ public void startUnbounded() { if (SubscriptionHelper.setOnce(upstream, EmptySubscription.INSTANCE)) { - queue = new SpscLinkedArrayQueue(bufferSize); + queue = new SpscLinkedArrayQueue<>(bufferSize); } } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { if (SubscriptionHelper.setOnce(upstream, s)) { if (s instanceof QueueSubscription) { @SuppressWarnings("unchecked") @@ -281,22 +280,22 @@ public void onSubscribe(Subscription s) { } } - queue = new SpscArrayQueue(bufferSize); + queue = new SpscArrayQueue<>(bufferSize); s.request(bufferSize); } } @Override - public void onNext(T t) { - if (once.get()) { + public void onNext(@NonNull T t) { + if (done) { return; } if (fusionMode == QueueSubscription.NONE) { ExceptionHelper.nullCheck(t, "onNext called with a null value."); if (!queue.offer(t)) { SubscriptionHelper.cancel(upstream); - onError(new MissingBackpressureException()); + onError(MissingBackpressureException.createDefault()); return; } } @@ -306,66 +305,72 @@ public void onNext(T t) { /** * Tries to offer an item into the internal queue and returns false * if the queue is full. - * @param t the item to offer, not null + * @param t the item to offer, not {@code null} * @return true if successful, false if the queue is full + * @throws NullPointerException if {@code t} is {@code null} + * @throws IllegalStateException if the processor is in fusion mode */ - public boolean offer(T t) { - if (once.get()) { + @CheckReturnValue + public boolean offer(@NonNull T t) { + ExceptionHelper.nullCheck(t, "offer called with a null value."); + if (done) { return false; } - ExceptionHelper.nullCheck(t, "offer called with a null value."); if (fusionMode == QueueSubscription.NONE) { if (queue.offer(t)) { drain(); return true; } + return false; } - return false; + throw new IllegalStateException("offer() should not be called in fusion mode!"); } @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { ExceptionHelper.nullCheck(t, "onError called with a null Throwable."); - if (once.compareAndSet(false, true)) { + if (!done) { error = t; done = true; drain(); - } else { - RxJavaPlugins.onError(t); + return; } + RxJavaPlugins.onError(t); } @Override public void onComplete() { - if (once.compareAndSet(false, true)) { - done = true; - drain(); - } + done = true; + drain(); } @Override + @CheckReturnValue public boolean hasSubscribers() { return subscribers.get().length != 0; } @Override + @CheckReturnValue public boolean hasThrowable() { - return once.get() && error != null; + return done && error != null; } @Override + @CheckReturnValue public boolean hasComplete() { - return once.get() && error == null; + return done && error == null; } @Override + @CheckReturnValue public Throwable getThrowable() { - return once.get() ? error : null; + return done ? error : null; } @Override - protected void subscribeActual(Subscriber s) { - MulticastSubscription ms = new MulticastSubscription(s, this); + protected void subscribeActual(@NonNull Subscriber s) { + MulticastSubscription ms = new MulticastSubscription<>(s, this); s.onSubscribe(ms); if (add(ms)) { if (ms.get() == Long.MIN_VALUE) { @@ -374,7 +379,7 @@ protected void subscribeActual(Subscriber s) { drain(); } } else { - if (once.get() || !refcount) { + if (done) { Throwable ex = error; if (ex != null) { s.onError(ex); @@ -427,7 +432,7 @@ void remove(MulticastSubscription inner) { if (refcount) { if (subscribers.compareAndSet(a, TERMINATED)) { SubscriptionHelper.cancel(upstream); - once.set(true); + done = true; break; } } else { @@ -550,7 +555,7 @@ void drain() { } if (as != bs) { - continue outer; + continue; } if (done && q.isEmpty()) { @@ -578,7 +583,7 @@ void drain() { } } - static final class MulticastSubscription extends AtomicLong implements Subscription { + static final class MulticastSubscription<@NonNull T> extends AtomicLong implements Subscription { private static final long serialVersionUID = -363282618957264509L; @@ -596,19 +601,9 @@ static final class MulticastSubscription extends AtomicLong implements Subscr @Override public void request(long n) { if (SubscriptionHelper.validate(n)) { - for (;;) { - long r = get(); - if (r == Long.MIN_VALUE || r == Long.MAX_VALUE) { - break; - } - long u = r + n; - if (u < 0L) { - u = Long.MAX_VALUE; - } - if (compareAndSet(r, u)) { - parent.drain(); - break; - } + long r = BackpressureHelper.addCancel(this, n); + if (r != Long.MIN_VALUE && r != Long.MAX_VALUE) { + parent.drain(); } } } diff --git a/src/main/java/io/reactivex/rxjava3/processors/PublishProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/PublishProcessor.java index 2d44f60f05..73507844d2 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/PublishProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/PublishProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.processors; import java.util.concurrent.atomic.*; @@ -71,7 +72,7 @@ *

*
Backpressure:
*
The processor does not coordinate backpressure for its subscribers and implements a weaker {@code onSubscribe} which - * calls requests Long.MAX_VALUE from the incoming Subscriptions. This makes it possible to subscribe the {@code PublishProcessor} + * calls requests {@link Long#MAX_VALUE} from the incoming Subscriptions. This makes it possible to subscribe the {@code PublishProcessor} * to multiple sources (note on serialization though) unlike the standard {@code Subscriber} contract. Child subscribers, however, are not overflown but receive an * {@link IllegalStateException} in case their requested amount is zero.
*
Scheduler:
@@ -106,7 +107,7 @@ * @param the value type multicasted to Subscribers. * @see MulticastProcessor */ -public final class PublishProcessor extends FlowableProcessor { +public final class PublishProcessor<@NonNull T> extends FlowableProcessor { /** The terminated indicator for the subscribers array. */ @SuppressWarnings("rawtypes") static final PublishSubscription[] TERMINATED = new PublishSubscription[0]; @@ -128,7 +129,7 @@ public final class PublishProcessor extends FlowableProcessor { @CheckReturnValue @NonNull public static PublishProcessor create() { - return new PublishProcessor(); + return new PublishProcessor<>(); } /** @@ -137,12 +138,12 @@ public static PublishProcessor create() { */ @SuppressWarnings("unchecked") PublishProcessor() { - subscribers = new AtomicReference[]>(EMPTY); + subscribers = new AtomicReference<>(EMPTY); } @Override - protected void subscribeActual(Subscriber t) { - PublishSubscription ps = new PublishSubscription(t, this); + protected void subscribeActual(@NonNull Subscriber t) { + PublishSubscription ps = new PublishSubscription<>(t, this); t.onSubscribe(ps); if (add(ps)) { // if cancellation happened while a successful add, the remove() didn't work @@ -226,7 +227,7 @@ void remove(PublishSubscription ps) { } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { if (subscribers.get() == TERMINATED) { s.cancel(); return; @@ -236,7 +237,7 @@ public void onSubscribe(Subscription s) { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { ExceptionHelper.nullCheck(t, "onNext called with a null value."); for (PublishSubscription s : subscribers.get()) { s.onNext(t); @@ -245,7 +246,7 @@ public void onNext(T t) { @SuppressWarnings("unchecked") @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { ExceptionHelper.nullCheck(t, "onError called with a null Throwable."); if (subscribers.get() == TERMINATED) { RxJavaPlugins.onError(t); @@ -270,24 +271,21 @@ public void onComplete() { } /** - * Tries to emit the item to all currently subscribed Subscribers if all of them - * has requested some value, returns false otherwise. - *

- * This method should be called in a sequential manner just like the onXXX methods - * of the PublishProcessor. + * Tries to emit the item to all currently subscribed {@link Subscriber}s if all of them + * has requested some value, returns {@code false} otherwise. *

- * Calling with a null value will terminate the PublishProcessor and a NullPointerException - * is signaled to the Subscribers. + * This method should be called in a sequential manner just like the {@code onXXX} methods + * of this {@code PublishProcessor}. *

History: 2.0.8 - experimental - * @param t the item to emit, not null - * @return true if the item was emitted to all Subscribers + * @param t the item to emit, not {@code null} + * @return {@code true} if the item was emitted to all {@code Subscriber}s + * @throws NullPointerException if {@code t} is {@code null} * @since 2.2 */ - public boolean offer(T t) { - if (t == null) { - onError(ExceptionHelper.createNullPointerException("offer called with a null value.")); - return true; - } + @CheckReturnValue + public boolean offer(@NonNull T t) { + ExceptionHelper.nullCheck(t, "offer called with a null value."); + PublishSubscription[] array = subscribers.get(); for (PublishSubscription s : array) { @@ -303,12 +301,14 @@ public boolean offer(T t) { } @Override + @CheckReturnValue public boolean hasSubscribers() { return subscribers.get().length != 0; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { if (subscribers.get() == TERMINATED) { return error; @@ -317,11 +317,13 @@ public Throwable getThrowable() { } @Override + @CheckReturnValue public boolean hasThrowable() { return subscribers.get() == TERMINATED && error != null; } @Override + @CheckReturnValue public boolean hasComplete() { return subscribers.get() == TERMINATED && error == null; } @@ -332,7 +334,7 @@ public boolean hasComplete() { * * @param the value type */ - static final class PublishSubscription extends AtomicLong implements Subscription { + static final class PublishSubscription<@NonNull T> extends AtomicLong implements Subscription { private static final long serialVersionUID = 3562861878281475070L; /** The actual subscriber. */ @@ -360,7 +362,7 @@ public void onNext(T t) { BackpressureHelper.producedCancel(this, 1); } else { cancel(); - downstream.onError(new MissingBackpressureException("Could not emit value due to lack of requests")); + downstream.onError(MissingBackpressureException.createDefault()); } } diff --git a/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java index 1ab964a6dd..ae6a7d3960 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/ReplayProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ /** * Replays events to Subscribers. *

- * The {@code ReplayProcessor} supports the following item retainment strategies: + * The {@code ReplayProcessor} supports the following item retention strategies: *

    *
  • {@link #create()} and {@link #create(int)}: retains and replays all events to current and * future {@code Subscriber}s. @@ -66,7 +66,7 @@ *

    * This {@code ReplayProcessor} respects the individual backpressure behavior of its {@code Subscriber}s but * does not coordinate their request amounts towards the upstream (because there might not be any) and - * consumes the upstream in an unbounded manner (requesting {@code Long.MAX_VALUE}). + * consumes the upstream in an unbounded manner (requesting {@link Long#MAX_VALUE}). * Note that {@code Subscriber}s receive a continuous sequence of values after they subscribed even * if an individual item gets delayed due to backpressure. * Due to concurrency requirements, a size-bounded {@code ReplayProcessor} may hold strong references to more source @@ -104,7 +104,7 @@ *

    Backpressure:
    *
    This {@code ReplayProcessor} respects the individual backpressure behavior of its {@code Subscriber}s but * does not coordinate their request amounts towards the upstream (because there might not be any) and - * consumes the upstream in an unbounded manner (requesting {@code Long.MAX_VALUE}). + * consumes the upstream in an unbounded manner (requesting {@link Long#MAX_VALUE}). * Note that {@code Subscriber}s receive a continuous sequence of values after they subscribed even * if an individual item gets delayed due to backpressure.
    *
    Scheduler:
    @@ -141,7 +141,7 @@ * * @param the value type */ -public final class ReplayProcessor extends FlowableProcessor { +public final class ReplayProcessor<@NonNull T> extends FlowableProcessor { /** An empty array to avoid allocation in getValues(). */ private static final Object[] EMPTY_ARRAY = new Object[0]; @@ -173,7 +173,7 @@ public final class ReplayProcessor extends FlowableProcessor { @CheckReturnValue @NonNull public static ReplayProcessor create() { - return new ReplayProcessor(new UnboundedReplayBuffer(16)); + return new ReplayProcessor<>(new UnboundedReplayBuffer<>(16)); } /** @@ -190,11 +190,13 @@ public static ReplayProcessor create() { * @param capacityHint * the initial buffer capacity * @return the created processor + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull public static ReplayProcessor create(int capacityHint) { - return new ReplayProcessor(new UnboundedReplayBuffer(capacityHint)); + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + return new ReplayProcessor<>(new UnboundedReplayBuffer<>(capacityHint)); } /** @@ -216,11 +218,13 @@ public static ReplayProcessor create(int capacityHint) { * @param maxSize * the maximum number of buffered items * @return the created processor + * @throws IllegalArgumentException if {@code maxSize} is non-positive */ @CheckReturnValue @NonNull public static ReplayProcessor createWithSize(int maxSize) { - return new ReplayProcessor(new SizeBoundReplayBuffer(maxSize)); + ObjectHelper.verifyPositive(maxSize, "maxSize"); + return new ReplayProcessor<>(new SizeBoundReplayBuffer<>(maxSize)); } /** @@ -236,8 +240,9 @@ public static ReplayProcessor createWithSize(int maxSize) { * the type of items observed and emitted by this type of processor * @return the created processor */ + @CheckReturnValue /* test */ static ReplayProcessor createUnbounded() { - return new ReplayProcessor(new SizeBoundReplayBuffer(Integer.MAX_VALUE)); + return new ReplayProcessor<>(new SizeBoundReplayBuffer<>(Integer.MAX_VALUE)); } /** @@ -271,11 +276,16 @@ public static ReplayProcessor createWithSize(int maxSize) { * @param scheduler * the {@link Scheduler} that provides the current time * @return the created processor + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code maxAge} is non-positive */ @CheckReturnValue @NonNull - public static ReplayProcessor createWithTime(long maxAge, TimeUnit unit, Scheduler scheduler) { - return new ReplayProcessor(new SizeAndTimeBoundReplayBuffer(Integer.MAX_VALUE, maxAge, unit, scheduler)); + public static ReplayProcessor createWithTime(long maxAge, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + ObjectHelper.verifyPositive(maxAge, "maxAge"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return new ReplayProcessor<>(new SizeAndTimeBoundReplayBuffer<>(Integer.MAX_VALUE, maxAge, unit, scheduler)); } /** @@ -311,11 +321,17 @@ public static ReplayProcessor createWithTime(long maxAge, TimeUnit unit, * @param scheduler * the {@link Scheduler} that provides the current time * @return the created processor + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code maxAge} or {@code maxSize} is non-positive */ @CheckReturnValue @NonNull - public static ReplayProcessor createWithTimeAndSize(long maxAge, TimeUnit unit, Scheduler scheduler, int maxSize) { - return new ReplayProcessor(new SizeAndTimeBoundReplayBuffer(maxSize, maxAge, unit, scheduler)); + public static ReplayProcessor createWithTimeAndSize(long maxAge, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, int maxSize) { + ObjectHelper.verifyPositive(maxSize, "maxSize"); + ObjectHelper.verifyPositive(maxAge, "maxAge"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return new ReplayProcessor<>(new SizeAndTimeBoundReplayBuffer<>(maxSize, maxAge, unit, scheduler)); } /** @@ -325,12 +341,12 @@ public static ReplayProcessor createWithTimeAndSize(long maxAge, TimeUnit @SuppressWarnings("unchecked") ReplayProcessor(ReplayBuffer buffer) { this.buffer = buffer; - this.subscribers = new AtomicReference[]>(EMPTY); + this.subscribers = new AtomicReference<>(EMPTY); } @Override protected void subscribeActual(Subscriber s) { - ReplaySubscription rs = new ReplaySubscription(s, this); + ReplaySubscription rs = new ReplaySubscription<>(s, this); s.onSubscribe(rs); if (add(rs)) { @@ -404,16 +420,19 @@ public void onComplete() { } @Override + @CheckReturnValue public boolean hasSubscribers() { return subscribers.get().length != 0; } + @CheckReturnValue /* test */ int subscriberCount() { return subscribers.get().length; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { ReplayBuffer b = buffer; if (b.isDone()) { @@ -445,6 +464,7 @@ public void cleanupBuffer() { *

    The method is thread-safe. * @return the latest value this processor currently has or null if no such value exists */ + @CheckReturnValue public T getValue() { return buffer.getValue(); } @@ -454,6 +474,7 @@ public T getValue() { *

    The method is thread-safe. * @return the array containing the snapshot of all values of this processor */ + @CheckReturnValue public Object[] getValues() { @SuppressWarnings("unchecked") T[] a = (T[])EMPTY_ARRAY; @@ -473,17 +494,20 @@ public Object[] getValues() { * @param array the target array to copy values into if it fits * @return the given array if the values fit into it or a new array containing all values */ + @CheckReturnValue public T[] getValues(T[] array) { return buffer.getValues(array); } @Override + @CheckReturnValue public boolean hasComplete() { ReplayBuffer b = buffer; return b.isDone() && b.getError() == null; } @Override + @CheckReturnValue public boolean hasThrowable() { ReplayBuffer b = buffer; return b.isDone() && b.getError() != null; @@ -494,10 +518,12 @@ public boolean hasThrowable() { *

    The method is thread-safe. * @return true if the processor has any value */ + @CheckReturnValue public boolean hasValue() { return buffer.size() != 0; // NOPMD } + @CheckReturnValue /* test*/ int size() { return buffer.size(); } @@ -558,7 +584,7 @@ void remove(ReplaySubscription rs) { * * @param the value type */ - interface ReplayBuffer { + interface ReplayBuffer<@NonNull T> { void next(T value); @@ -586,7 +612,7 @@ interface ReplayBuffer { void trimHead(); } - static final class ReplaySubscription extends AtomicInteger implements Subscription { + static final class ReplaySubscription<@NonNull T> extends AtomicInteger implements Subscription { private static final long serialVersionUID = 466549804534799122L; final Subscriber downstream; @@ -634,7 +660,7 @@ static final class UnboundedReplayBuffer volatile int size; UnboundedReplayBuffer(int capacityHint) { - this.buffer = new ArrayList(ObjectHelper.verifyPositive(capacityHint, "capacityHint")); + this.buffer = new ArrayList<>(capacityHint); } @Override @@ -820,7 +846,7 @@ static final class TimedNode extends AtomicReference> { } } - static final class SizeBoundReplayBuffer + static final class SizeBoundReplayBuffer<@NonNull T> implements ReplayBuffer { final int maxSize; @@ -834,8 +860,8 @@ static final class SizeBoundReplayBuffer volatile boolean done; SizeBoundReplayBuffer(int maxSize) { - this.maxSize = ObjectHelper.verifyPositive(maxSize, "maxSize"); - Node h = new Node(null); + this.maxSize = maxSize; + Node h = new Node<>(null); this.tail = h; this.head = h; } @@ -850,7 +876,7 @@ void trim() { @Override public void next(T value) { - Node n = new Node(value); + Node n = new Node<>(value); Node t = tail; tail = n; @@ -876,7 +902,7 @@ public void complete() { @Override public void trimHead() { if (head.value != null) { - Node n = new Node(null); + Node n = new Node<>(null); n.lazySet(head.get()); head = n; } @@ -1050,11 +1076,11 @@ static final class SizeAndTimeBoundReplayBuffer volatile boolean done; SizeAndTimeBoundReplayBuffer(int maxSize, long maxAge, TimeUnit unit, Scheduler scheduler) { - this.maxSize = ObjectHelper.verifyPositive(maxSize, "maxSize"); - this.maxAge = ObjectHelper.verifyPositive(maxAge, "maxAge"); - this.unit = ObjectHelper.requireNonNull(unit, "unit is null"); - this.scheduler = ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - TimedNode h = new TimedNode(null, 0L); + this.maxSize = maxSize; + this.maxAge = maxAge; + this.unit = unit; + this.scheduler = scheduler; + TimedNode h = new TimedNode<>(null, 0L); this.tail = h; this.head = h; } @@ -1075,10 +1101,6 @@ void trim() { break; } TimedNode next = h.get(); - if (next == null) { - head = h; - break; - } if (next.time > limit) { head = h; @@ -1100,7 +1122,7 @@ void trimFinal() { TimedNode next = h.get(); if (next == null) { if (h.value != null) { - head = new TimedNode(null, 0L); + head = new TimedNode<>(null, 0L); } else { head = h; } @@ -1109,7 +1131,7 @@ void trimFinal() { if (next.time > limit) { if (h.value != null) { - TimedNode n = new TimedNode(null, 0L); + TimedNode n = new TimedNode<>(null, 0L); n.lazySet(h.get()); head = n; } else { @@ -1125,7 +1147,7 @@ void trimFinal() { @Override public void trimHead() { if (head.value != null) { - TimedNode n = new TimedNode(null, 0L); + TimedNode n = new TimedNode<>(null, 0L); n.lazySet(head.get()); head = n; } @@ -1133,7 +1155,7 @@ public void trimHead() { @Override public void next(T value) { - TimedNode n = new TimedNode(value, scheduler.now(unit)); + TimedNode n = new TimedNode<>(value, scheduler.now(unit)); TimedNode t = tail; tail = n; diff --git a/src/main/java/io/reactivex/rxjava3/processors/SerializedProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/SerializedProcessor.java index 6096398618..e40d935c51 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/SerializedProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/SerializedProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -59,7 +59,7 @@ public void onSubscribe(Subscription s) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(NotificationLite.subscription(s)); @@ -92,7 +92,7 @@ public void onNext(T t) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(NotificationLite.next(t)); @@ -119,7 +119,7 @@ public void onError(Throwable t) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.setFirst(NotificationLite.error(t)); @@ -149,7 +149,7 @@ public void onComplete() { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(NotificationLite.complete()); diff --git a/src/main/java/io/reactivex/rxjava3/processors/UnicastProcessor.java b/src/main/java/io/reactivex/rxjava3/processors/UnicastProcessor.java index a3715103d5..ad7e7f66b6 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/UnicastProcessor.java +++ b/src/main/java/io/reactivex/rxjava3/processors/UnicastProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,16 +13,17 @@ package io.reactivex.rxjava3.processors; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.*; import io.reactivex.rxjava3.annotations.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; +import io.reactivex.rxjava3.internal.functions.*; import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.*; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** @@ -66,7 +67,7 @@ * {@link NullPointerException} being thrown and the processor's state is not changed. *

    * Since a {@code UnicastProcessor} is a {@link io.reactivex.rxjava3.core.Flowable} as well as a {@link FlowableProcessor}, it - * honors the downstream backpressure but consumes an upstream source in an unbounded manner (requesting {@code Long.MAX_VALUE}). + * honors the downstream backpressure but consumes an upstream source in an unbounded manner (requesting {@link Long#MAX_VALUE}). *

    * When this {@code UnicastProcessor} is terminated via {@link #onError(Throwable)} the current or late single {@code Subscriber} * may receive the {@code Throwable} before any available items could be emitted. To make sure an {@code onError} event is delivered @@ -90,7 +91,7 @@ *

    *
    Backpressure:
    *
    {@code UnicastProcessor} honors the downstream backpressure but consumes an upstream source - * (if any) in an unbounded manner (requesting {@code Long.MAX_VALUE}).
    + * (if any) in an unbounded manner (requesting {@link Long#MAX_VALUE}). *
    Scheduler:
    *
    {@code UnicastProcessor} does not operate by default on a particular {@link io.reactivex.rxjava3.core.Scheduler} and * the single {@code Subscriber} gets notified on the thread the respective {@code onXXX} methods were invoked.
    @@ -146,7 +147,7 @@ * @param the value type received and emitted by this Processor subclass * @since 2.0 */ -public final class UnicastProcessor extends FlowableProcessor { +public final class UnicastProcessor<@NonNull T> extends FlowableProcessor { final SpscLinkedArrayQueue queue; @@ -178,7 +179,7 @@ public final class UnicastProcessor extends FlowableProcessor { @CheckReturnValue @NonNull public static UnicastProcessor create() { - return new UnicastProcessor(bufferSize()); + return new UnicastProcessor<>(bufferSize(), null, true); } /** @@ -186,11 +187,13 @@ public static UnicastProcessor create() { * @param the value type * @param capacityHint the hint to size the internal unbounded buffer * @return an UnicastProcessor instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull public static UnicastProcessor create(int capacityHint) { - return new UnicastProcessor(capacityHint); + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + return new UnicastProcessor<>(capacityHint, null, true); } /** @@ -204,67 +207,53 @@ public static UnicastProcessor create(int capacityHint) { @CheckReturnValue @NonNull public static UnicastProcessor create(boolean delayError) { - return new UnicastProcessor(bufferSize(), null, delayError); + return new UnicastProcessor<>(bufferSize(), null, delayError); } /** * Creates an UnicastProcessor with the given internal buffer capacity hint and a callback for - * the case when the single Subscriber cancels its subscription. + * the case when the single Subscriber cancels its subscription or the + * processor is terminated. * *

    The callback, if not null, is called exactly once and * non-overlapped with any active replay. * * @param the value type * @param capacityHint the hint to size the internal unbounded buffer - * @param onCancelled the non null callback + * @param onTerminate the non null callback * @return an UnicastProcessor instance + * @throws NullPointerException if {@code onTerminate} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull - public static UnicastProcessor create(int capacityHint, Runnable onCancelled) { - ObjectHelper.requireNonNull(onCancelled, "onTerminate"); - return new UnicastProcessor(capacityHint, onCancelled); + public static UnicastProcessor create(int capacityHint, @NonNull Runnable onTerminate) { + return create(capacityHint, onTerminate, true); } /** * Creates an UnicastProcessor with the given internal buffer capacity hint, delay error flag and a callback for - * the case when the single Subscriber cancels its subscription. + * the case when the single Subscriber cancels its subscription or + * the processor is terminated. * *

    The callback, if not null, is called exactly once and * non-overlapped with any active replay. *

    History: 2.0.8 - experimental * @param the value type * @param capacityHint the hint to size the internal unbounded buffer - * @param onCancelled the non null callback + * @param onTerminate the non null callback * @param delayError deliver pending onNext events before onError * @return an UnicastProcessor instance + * @throws NullPointerException if {@code onTerminate} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @since 2.2 */ @CheckReturnValue @NonNull - public static UnicastProcessor create(int capacityHint, Runnable onCancelled, boolean delayError) { - ObjectHelper.requireNonNull(onCancelled, "onTerminate"); - return new UnicastProcessor(capacityHint, onCancelled, delayError); - } - - /** - * Creates an UnicastProcessor with the given capacity hint. - * @param capacityHint the capacity hint for the internal, unbounded queue - * @since 2.0 - */ - UnicastProcessor(int capacityHint) { - this(capacityHint, null, true); - } - - /** - * Creates an UnicastProcessor with the given capacity hint and callback - * for when the Processor is terminated normally or its single Subscriber cancels. - * @param capacityHint the capacity hint for the internal, unbounded queue - * @param onTerminate the callback to run when the Processor is terminated or cancelled, null not allowed - * @since 2.0 - */ - UnicastProcessor(int capacityHint, Runnable onTerminate) { - this(capacityHint, onTerminate, true); + public static UnicastProcessor create(int capacityHint, @NonNull Runnable onTerminate, boolean delayError) { + Objects.requireNonNull(onTerminate, "onTerminate"); + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + return new UnicastProcessor<>(capacityHint, onTerminate, delayError); } /** @@ -277,10 +266,10 @@ public static UnicastProcessor create(int capacityHint, Runnable onCancel * @since 2.2 */ UnicastProcessor(int capacityHint, Runnable onTerminate, boolean delayError) { - this.queue = new SpscLinkedArrayQueue(ObjectHelper.verifyPositive(capacityHint, "capacityHint")); - this.onTerminate = new AtomicReference(onTerminate); + this.queue = new SpscLinkedArrayQueue<>(capacityHint); + this.onTerminate = new AtomicReference<>(onTerminate); this.delayError = delayError; - this.downstream = new AtomicReference>(); + this.downstream = new AtomicReference<>(); this.once = new AtomicBoolean(); this.wip = new UnicastQueueSubscription(); this.requested = new AtomicLong(); @@ -558,12 +547,14 @@ public void cancel() { } @Override + @CheckReturnValue public boolean hasSubscribers() { return downstream.get() != null; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { if (done) { return error; @@ -572,11 +563,13 @@ public Throwable getThrowable() { } @Override + @CheckReturnValue public boolean hasComplete() { return done && error == null; } @Override + @CheckReturnValue public boolean hasThrowable() { return done && error != null; } diff --git a/src/main/java/io/reactivex/rxjava3/processors/package-info.java b/src/main/java/io/reactivex/rxjava3/processors/package-info.java index be2bb7c954..f4119c9ba8 100644 --- a/src/main/java/io/reactivex/rxjava3/processors/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/processors/package-info.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ /** diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/SchedulerRunnableIntrospection.java b/src/main/java/io/reactivex/rxjava3/schedulers/SchedulerRunnableIntrospection.java index aec3170138..97f8efcc9d 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/SchedulerRunnableIntrospection.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/SchedulerRunnableIntrospection.java @@ -1,8 +1,11 @@ -/** +/* * 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/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java index 254511f5e0..cb25652404 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Schedulers.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,22 +22,27 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Static factory methods for returning standard Scheduler instances. + * Static factory methods for returning standard {@link Scheduler} instances. *

    * The initial and runtime values of the various scheduler types can be overridden via the * {@code RxJavaPlugins.setInit(scheduler name)SchedulerHandler()} and * {@code RxJavaPlugins.set(scheduler name)SchedulerHandler()} respectively. + * Note that overriding any initial {@code Scheduler} via the {@link RxJavaPlugins} + * has to happen before the {@code Schedulers} class is accessed. *

    * Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@link #io()} Scheduler workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
    • - *
    • {@code rx3.io-priority} (int): sets the thread priority of the {@link #io()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • - *
    • {@code rx3.computation-threads} (int): sets the number of threads in the {@link #computation()} Scheduler, default is the number of available CPUs
    • - *
    • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • - *
    • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • - *
    • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • - *
    • {@code rx3.purge-enabled} (boolean): enables periodic purging of all Scheduler's backing thread pools, default is false
    • - *
    • {@code rx3.purge-period-seconds} (int): specifies the periodic purge interval of all Scheduler's backing thread pools, default is 1 second
    • + *
    • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@link #io()} {@code Scheduler} workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
    • + *
    • {@code rx3.io-priority} (int): sets the thread priority of the {@link #io()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.io-scheduled-release} (boolean): {@code true} sets the worker release mode of the + * {@link #io()} {@code Scheduler} to scheduled, default is {@code false} for eager mode.
    • + *
    • {@code rx3.computation-threads} (int): sets the number of threads in the {@link #computation()} {@code Scheduler}, default is the number of available CPUs
    • + *
    • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.purge-enabled} (boolean): enables purging of all {@code Scheduler}'s backing thread pools, default is {@code true}
    • + *
    • {@code rx3.scheduler.use-nanotime} (boolean): {@code true} instructs {@code Scheduler} to use {@link System#nanoTime()} for {@link Scheduler#now(TimeUnit)}, + * instead of default {@link System#currentTimeMillis()} ({@code false})
    • *
    */ public final class Schedulers { @@ -105,32 +110,32 @@ private Schedulers() { * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to true, attempting to execute + * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to {@code true}, attempting to execute * operators that block while running on this scheduler will throw an {@link IllegalStateException}. *

    * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

    Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.computation-threads} (int): sets the number of threads in the {@link #computation()} Scheduler, default is the number of available CPUs
    • - *
    • {@code rx3.computation-priority} (int): sets the thread priority of the {@link #computation()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.computation-threads} (int): sets the number of threads in the {@code computation()} {@code Scheduler}, default is the number of available CPUs
    • + *
    • {@code rx3.computation-priority} (int): sets the thread priority of the {@code computation()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitComputationSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setComputationSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

    - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createComputationScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#COMPUTATION COMPUTATION}) * annotation. - * @return a {@link Scheduler} meant for computation-bound work + * @return a {@code Scheduler} meant for computation-bound work */ @NonNull public static Scheduler computation() { @@ -146,7 +151,7 @@ public static Scheduler computation() { * that will try to reuse previously started instances used by the worker * returned by {@link io.reactivex.rxjava3.core.Scheduler#createWorker()} but otherwise will start a new backing * {@link ScheduledExecutorService} instance. Note that this scheduler may create an unbounded number - * of worker threads that can result in system slowdowns or {@code OutOfMemoryError}. Therefore, for casual uses + * of worker threads that can result in system slowdowns or {@link OutOfMemoryError}. Therefore, for casual uses * or when implementing an operator, the Worker instances must be disposed via {@link io.reactivex.rxjava3.core.Scheduler.Worker#dispose()}. *

    * It is not recommended to perform computational work on this scheduler. Use {@link #computation()} instead. @@ -154,28 +159,46 @@ public static Scheduler computation() { * Unhandled errors will be delivered to the scheduler Thread's {@link java.lang.Thread.UncaughtExceptionHandler}. *

    * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

    Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@link #io()} Scheduler workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
    • - *
    • {@code rx3.io-priority} (int): sets the thread priority of the {@link #io()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.io-keep-alive-time} (long): sets the keep-alive time of the {@code io()} {@code Scheduler} workers, default is {@link IoScheduler#KEEP_ALIVE_TIME_DEFAULT}
    • + *
    • {@code rx3.io-priority} (int): sets the thread priority of the {@code io()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.io-scheduled-release} (boolean): {@code true} sets the worker release mode of the + * {@code #io()} {@code Scheduler} to scheduled, default is {@code false} for eager mode.
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitIoSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setIoSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

    - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createIoScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#IO IO}) * annotation. - * @return a {@link Scheduler} meant for IO-bound work + *

    + * When the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} is disposed, + * the underlying worker can be released to the cached worker pool in two modes: + *

      + *
    • In eager mode (default), the underlying worker is returned immediately to the cached worker pool + * and can be reused much quicker by operators. The drawback is that if the currently running task doesn't + * respond to interruption in time or at all, this may lead to delays or deadlock with the reuse use of the + * underlying worker. + *
    • + *
    • In scheduled mode (enabled via the system parameter {@code rx3.io-scheduled-release} + * set to {@code true}), the underlying worker is returned to the cached worker pool only after the currently running task + * has finished. This can help prevent premature reuse of the underlying worker and likely won't lead to delays or + * deadlock with such reuses. The drawback is that the delay in release may lead to an excess amount of underlying + * workers being created. + *
    • + *
    + * @return a {@code Scheduler} meant for IO-bound work */ @NonNull public static Scheduler io() { @@ -194,7 +217,7 @@ public static Scheduler io() { * by RxJava itself but may be found in external libraries. *

    * This scheduler can't be overridden via an {@link RxJavaPlugins} method. - * @return a {@link Scheduler} that queues work on the current thread + * @return a {@code Scheduler} that queues work on the current thread */ @NonNull public static Scheduler trampoline() { @@ -207,33 +230,33 @@ public static Scheduler trampoline() { * The default implementation of this scheduler creates a new, single-threaded {@link ScheduledExecutorService} for * each invocation of the {@link Scheduler#scheduleDirect(Runnable)} (plus its overloads) and {@link Scheduler#createWorker()} * methods, thus an unbounded number of worker threads may be created that can - * result in system slowdowns or {@code OutOfMemoryError}. Therefore, for casual uses or when implementing an operator, + * result in system slowdowns or {@link OutOfMemoryError}. Therefore, for casual uses or when implementing an operator, * the Worker instances must be disposed via {@link io.reactivex.rxjava3.core.Scheduler.Worker#dispose()}. *

    * Unhandled errors will be delivered to the scheduler Thread's {@link java.lang.Thread.UncaughtExceptionHandler}. *

    * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

    Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.newthread-priority} (int): sets the thread priority of the {@link #newThread()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.newthread-priority} (int): sets the thread priority of the {@code newThread()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitNewThreadSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setNewThreadSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

    - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createNewThreadScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#NEW_THREAD NEW_TRHEAD}) * annotation. - * @return a {@link Scheduler} that creates new threads + * @return a {@code Scheduler} that creates new threads */ @NonNull public static Scheduler newThread() { @@ -247,7 +270,7 @@ public static Scheduler newThread() { * Uses: *

      *
    • event loop
    • - *
    • support Schedulers.from(Executor) and from(ExecutorService) with delayed scheduling
    • + *
    • support {@code Schedulers.from(}{@link Executor}{@code )} and {@code from(}{@link ExecutorService}{@code )} with delayed scheduling
    • *
    • support benchmarks that pipeline data from some thread to another thread and * avoid core-bashing of computation's round-robin nature
    • *
    @@ -258,31 +281,31 @@ public static Scheduler newThread() { * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to true, attempting to execute + * If the {@link RxJavaPlugins#setFailOnNonBlockingScheduler(boolean)} is set to {@code true}, attempting to execute * operators that block while running on this scheduler will throw an {@link IllegalStateException}. *

    * You can control certain properties of this standard scheduler via system properties that have to be set - * before the {@link Schedulers} class is referenced in your code. + * before the {@code Schedulers} class is referenced in your code. *

    Supported system properties ({@code System.getProperty()}): *

      - *
    • {@code rx3.single-priority} (int): sets the thread priority of the {@link #single()} Scheduler, default is {@link Thread#NORM_PRIORITY}
    • + *
    • {@code rx3.single-priority} (int): sets the thread priority of the {@code single()} {@code Scheduler}, default is {@link Thread#NORM_PRIORITY}
    • *
    *

    * The default value of this scheduler can be overridden at initialization time via the * {@link RxJavaPlugins#setInitSingleSchedulerHandler(io.reactivex.rxjava3.functions.Function)} plugin method. * Note that due to possible initialization cycles, using any of the other scheduler-returning methods will - * result in a {@code NullPointerException}. - * Once the {@link Schedulers} class has been initialized, you can override the returned {@link Scheduler} instance + * result in a {@link NullPointerException}. + * Once the {@code Schedulers} class has been initialized, you can override the returned {@code Scheduler} instance * via the {@link RxJavaPlugins#setSingleSchedulerHandler(io.reactivex.rxjava3.functions.Function)} method. *

    - * It is possible to create a fresh instance of this scheduler with a custom ThreadFactory, via the + * It is possible to create a fresh instance of this scheduler with a custom {@link ThreadFactory}, via the * {@link RxJavaPlugins#createSingleScheduler(ThreadFactory)} method. Note that such custom * instances require a manual call to {@link Scheduler#shutdown()} to allow the JVM to exit or the * (J2EE) container to unload properly. *

    Operators on the base reactive classes that use this scheduler are marked with the * @{@link io.reactivex.rxjava3.annotations.SchedulerSupport SchedulerSupport}({@link io.reactivex.rxjava3.annotations.SchedulerSupport#SINGLE SINGLE}) * annotation. - * @return a {@link Scheduler} that shares a single backing thread. + * @return a {@code Scheduler} that shares a single backing thread. * @since 2.0 */ @NonNull @@ -291,11 +314,11 @@ public static Scheduler single() { } /** - * Wraps an {@link Executor} into a new Scheduler instance and delegates {@code schedule()} + * Wraps an {@link Executor} into a new {@link Scheduler} instance and delegates {@code schedule()} * calls to it. *

    * If the provided executor doesn't support any of the more specific standard Java executor - * APIs, cancelling tasks scheduled by this scheduler can't be interrupted when they are + * APIs, tasks scheduled by this scheduler can't be interrupted when they are * executing but only prevented from running prior to that. In addition, tasks scheduled with * a time delay or periodically will use the {@link #single()} scheduler for the timed waiting * before posting the actual task to the given executor. @@ -304,24 +327,24 @@ public static Scheduler single() { * {@link #from(Executor, boolean)} overload to enable task interruption via this wrapper. *

    * If the provided executor supports the standard Java {@link ExecutorService} API, - * cancelling tasks scheduled by this scheduler can be cancelled/interrupted by calling + * tasks scheduled by this scheduler can be cancelled/interrupted by calling * {@link io.reactivex.rxjava3.disposables.Disposable#dispose()}. In addition, tasks scheduled with * a time delay or periodically will use the {@link #single()} scheduler for the timed waiting * before posting the actual task to the given executor. *

    * If the provided executor supports the standard Java {@link ScheduledExecutorService} API, - * cancelling tasks scheduled by this scheduler can be cancelled/interrupted by calling + * tasks scheduled by this scheduler can be cancelled/interrupted by calling * {@link io.reactivex.rxjava3.disposables.Disposable#dispose()}. In addition, tasks scheduled with * a time delay or periodically will use the provided executor. Note, however, if the provided * {@code ScheduledExecutorService} instance is not single threaded, tasks scheduled * with a time delay close to each other may end up executing in different order than * the original schedule() call was issued. This limitation may be lifted in a future patch. *

    - * The implementation of the Worker of this wrapper Scheduler is eager and will execute as many + * The implementation of the Worker of this wrapper {@code Scheduler} is eager and will execute as many * non-delayed tasks as it can, which may result in a longer than expected occupation of a - * thread of the given backing Executor. In other terms, it does not allow per-Runnable fairness - * in case the worker runs on a shared underlying thread of the Executor. - * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying Executor + * thread of the given backing {@code Executor}. In other terms, it does not allow per-{@link Runnable} fairness + * in case the worker runs on a shared underlying thread of the {@code Executor}. + * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying {@code Executor} * more fairly. *

    * Starting, stopping and restarting this scheduler is not supported (no-op) and the provided @@ -340,26 +363,40 @@ public static Scheduler single() { * } * *

    + * Note that the provided {@code Executor} should avoid throwing a {@link RejectedExecutionException} + * (for example, by shutting it down prematurely or using a bounded-queue {@code ExecutorService}) + * because such circumstances prevent RxJava from progressing flow-related activities correctly. + * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, + * the {@code RejectedExecutionException} is routed to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-related problems, it is recommended + * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying + * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, + * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. + *

    * This type of scheduler is less sensitive to leaking {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} instances, although * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * Note that this method returns a new {@link Scheduler} instance, even for the same {@link Executor} instance. + * Note that this method returns a new {@code Scheduler} instance, even for the same {@code Executor} instance. + *

    + * It is possible to wrap an {@code Executor} into a {@code Scheduler} without triggering the initialization of all the + * standard schedulers by using the {@link RxJavaPlugins#createExecutorScheduler(Executor, boolean, boolean)} method + * before the {@code Schedulers} class itself is accessed. * @param executor * the executor to wrap - * @return the new Scheduler wrapping the Executor + * @return the new {@code Scheduler} wrapping the {@code Executor} * @see #from(Executor, boolean, boolean) */ @NonNull public static Scheduler from(@NonNull Executor executor) { - return new ExecutorScheduler(executor, false, false); + return from(executor, false, false); } /** - * Wraps an {@link Executor} into a new Scheduler instance and delegates {@code schedule()} + * Wraps an {@link Executor} into a new {@link Scheduler} instance and delegates {@code schedule()} * calls to it. *

    - * The tasks scheduled by the returned {@link Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} + * The tasks scheduled by the returned {@code Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} * can be optionally interrupted. *

    * If the provided executor doesn't support any of the more specific standard Java executor @@ -368,24 +405,24 @@ public static Scheduler from(@NonNull Executor executor) { * before posting the actual task to the given executor. *

    * If the provided executor supports the standard Java {@link ExecutorService} API, - * canceling tasks scheduled by this scheduler can be cancelled/interrupted by calling + * tasks scheduled by this scheduler can be cancelled/interrupted by calling * {@link io.reactivex.rxjava3.disposables.Disposable#dispose()}. In addition, tasks scheduled with * a time delay or periodically will use the {@link #single()} scheduler for the timed waiting * before posting the actual task to the given executor. *

    * If the provided executor supports the standard Java {@link ScheduledExecutorService} API, - * canceling tasks scheduled by this scheduler can be cancelled/interrupted by calling + * tasks scheduled by this scheduler can be cancelled/interrupted by calling * {@link io.reactivex.rxjava3.disposables.Disposable#dispose()}. In addition, tasks scheduled with * a time delay or periodically will use the provided executor. Note, however, if the provided * {@code ScheduledExecutorService} instance is not single threaded, tasks scheduled * with a time delay close to each other may end up executing in different order than * the original schedule() call was issued. This limitation may be lifted in a future patch. *

    - * The implementation of the Worker of this wrapper Scheduler is eager and will execute as many + * The implementation of the {@code Worker} of this wrapper {@code Scheduler} is eager and will execute as many * non-delayed tasks as it can, which may result in a longer than expected occupation of a - * thread of the given backing Executor. In other terms, it does not allow per-Runnable fairness - * in case the worker runs on a shared underlying thread of the Executor. - * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying Executor + * thread of the given backing {@code Executor}. In other terms, it does not allow per-{@link Runnable} fairness + * in case the worker runs on a shared underlying thread of the {@code Executor}. + * See {@link #from(Executor, boolean, boolean)} to create a wrapper that uses the underlying {@code Executor} * more fairly. *

    * Starting, stopping and restarting this scheduler is not supported (no-op) and the provided @@ -404,30 +441,44 @@ public static Scheduler from(@NonNull Executor executor) { * } * *

    + * Note that the provided {@code Executor} should avoid throwing a {@link RejectedExecutionException} + * (for example, by shutting it down prematurely or using a bounded-queue {@code ExecutorService}) + * because such circumstances prevent RxJava from progressing flow-related activities correctly. + * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, + * the {@code RejectedExecutionException} is routed to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-related problems, it is recommended + * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying + * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, + * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. + *

    * This type of scheduler is less sensitive to leaking {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} instances, although * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * Note that this method returns a new {@link Scheduler} instance, even for the same {@link Executor} instance. + * Note that this method returns a new {@code Scheduler} instance, even for the same {@code Executor} instance. + *

    + * It is possible to wrap an {@code Executor} into a {@code Scheduler} without triggering the initialization of all the + * standard schedulers by using the {@link RxJavaPlugins#createExecutorScheduler(Executor, boolean, boolean)} method + * before the {@code Schedulers} class itself is accessed. *

    History: 2.2.6 - experimental * @param executor * the executor to wrap - * @param interruptibleWorker if {@code true} the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will + * @param interruptibleWorker if {@code true}, the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will * be interrupted when the task is disposed. - * @return the new Scheduler wrapping the Executor + * @return the new {@code Scheduler} wrapping the {@code Executor} * @since 3.0.0 * @see #from(Executor, boolean, boolean) */ @NonNull public static Scheduler from(@NonNull Executor executor, boolean interruptibleWorker) { - return new ExecutorScheduler(executor, interruptibleWorker, false); + return from(executor, interruptibleWorker, false); } /** - * Wraps an {@link Executor} into a new Scheduler instance and delegates {@code schedule()} + * Wraps an {@link Executor} into a new {@link Scheduler} instance and delegates {@code schedule()} * calls to it. *

    - * The tasks scheduled by the returned {@link Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} + * The tasks scheduled by the returned {@code Scheduler} and its {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} * can be optionally interrupted. *

    * If the provided executor doesn't support any of the more specific standard Java executor @@ -436,27 +487,27 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo * before posting the actual task to the given executor. *

    * If the provided executor supports the standard Java {@link ExecutorService} API, - * canceling tasks scheduled by this scheduler can be cancelled/interrupted by calling + * tasks scheduled by this scheduler can be cancelled/interrupted by calling * {@link io.reactivex.rxjava3.disposables.Disposable#dispose()}. In addition, tasks scheduled with * a time delay or periodically will use the {@link #single()} scheduler for the timed waiting * before posting the actual task to the given executor. *

    * If the provided executor supports the standard Java {@link ScheduledExecutorService} API, - * canceling tasks scheduled by this scheduler can be cancelled/interrupted by calling + * tasks scheduled by this scheduler can be cancelled/interrupted by calling * {@link io.reactivex.rxjava3.disposables.Disposable#dispose()}. In addition, tasks scheduled with * a time delay or periodically will use the provided executor. Note, however, if the provided * {@code ScheduledExecutorService} instance is not single threaded, tasks scheduled * with a time delay close to each other may end up executing in different order than * the original schedule() call was issued. This limitation may be lifted in a future patch. *

    - * The implementation of the Worker of this wrapper Scheduler can operate in both eager (non-fair) and + * The implementation of the Worker of this wrapper {@code Scheduler} can operate in both eager (non-fair) and * fair modes depending on the specified parameter. In eager mode, it will execute as many * non-delayed tasks as it can, which may result in a longer than expected occupation of a - * thread of the given backing Executor. In other terms, it does not allow per-Runnable fairness - * in case the worker runs on a shared underlying thread of the Executor. In fair mode, + * thread of the given backing {@code Executor}. In other terms, it does not allow per-{@link Runnable} fairness + * in case the worker runs on a shared underlying thread of the {@code Executor}. In fair mode, * non-delayed tasks will still be executed in a FIFO and non-overlapping manner, but after each task, - * the execution for the next task is rescheduled with the same underlying Executor, allowing interleaving - * from both the same Scheduler or other external usages of the underlying Executor. + * the execution for the next task is rescheduled with the same underlying {@code Executor}, allowing interleaving + * from both the same {@code Scheduler} or other external usages of the underlying {@code Executor}. *

    * Starting, stopping and restarting this scheduler is not supported (no-op) and the provided * executor's lifecycle must be managed externally: @@ -474,28 +525,43 @@ public static Scheduler from(@NonNull Executor executor, boolean interruptibleWo * } * *

    + * Note that the provided {@code Executor} should avoid throwing a {@link RejectedExecutionException} + * (for example, by shutting it down prematurely or using a bounded-queue {@code ExecutorService}) + * because such circumstances prevent RxJava from progressing flow-related activities correctly. + * If the {@link Executor#execute(Runnable)} or {@link ExecutorService#submit(Callable)} throws, + * the {@code RejectedExecutionException} is routed to the global error handler via + * {@link RxJavaPlugins#onError(Throwable)}. To avoid shutdown-related problems, it is recommended + * all flows using the returned {@code Scheduler} to be canceled/disposed before the underlying + * {@code Executor} is shut down. To avoid problems due to the {@code Executor} having a bounded-queue, + * it is recommended to rephrase the flow to utilize backpressure as the means to limit outstanding work. + *

    * This type of scheduler is less sensitive to leaking {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} instances, although * not disposing a worker that has timed/delayed tasks not cancelled by other means may leak resources and/or * execute those tasks "unexpectedly". *

    - * Note that this method returns a new {@link Scheduler} instance, even for the same {@link Executor} instance. + * Note that this method returns a new {@code Scheduler} instance, even for the same {@code Executor} instance. + *

    + * It is possible to wrap an {@code Executor} into a {@code Scheduler} without triggering the initialization of all the + * standard schedulers by using the {@link RxJavaPlugins#createExecutorScheduler(Executor, boolean, boolean)} method + * before the {@code Schedulers} class itself is accessed. + * * @param executor * the executor to wrap - * @param interruptibleWorker if {@code true} the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will + * @param interruptibleWorker if {@code true}, the tasks submitted to the {@link io.reactivex.rxjava3.core.Scheduler.Worker Scheduler.Worker} will * be interrupted when the task is disposed. - * @param fair if {@code true} tasks submitted to the will be executed by the underlying {@link Executor} one after the other, still + * @param fair if {@code true}, tasks submitted to the {@code Scheduler} or {@code Worker} will be executed by the underlying {@code Executor} one after the other, still * in a FIFO and non-overlapping manner, but allows interleaving with other tasks submitted to the underlying {@code Executor}. * If {@code false}, the underlying FIFO scheme will execute as many tasks as it can before giving up the underlying {@code Executor} thread. - * @return the new Scheduler wrapping the Executor + * @return the new {@code Scheduler} wrapping the {@code Executor} * @since 3.0.0 */ @NonNull public static Scheduler from(@NonNull Executor executor, boolean interruptibleWorker, boolean fair) { - return new ExecutorScheduler(executor, interruptibleWorker, fair); + return RxJavaPlugins.createExecutorScheduler(executor, interruptibleWorker, fair); } /** - * Shuts down the standard Schedulers. + * Shuts down the standard {@link Scheduler}s. *

    The operation is idempotent and thread-safe. */ public static void shutdown() { @@ -504,11 +570,10 @@ public static void shutdown() { newThread().shutdown(); single().shutdown(); trampoline().shutdown(); - SchedulerPoolFactory.shutdown(); } /** - * Starts the standard Schedulers. + * Starts the standard {@link Scheduler}s. *

    The operation is idempotent and thread-safe. */ public static void start() { @@ -517,33 +582,32 @@ public static void start() { newThread().start(); single().start(); trampoline().start(); - SchedulerPoolFactory.start(); } static final class IOTask implements Supplier { @Override - public Scheduler get() throws Exception { + public Scheduler get() { return IoHolder.DEFAULT; } } static final class NewThreadTask implements Supplier { @Override - public Scheduler get() throws Exception { + public Scheduler get() { return NewThreadHolder.DEFAULT; } } static final class SingleTask implements Supplier { @Override - public Scheduler get() throws Exception { + public Scheduler get() { return SingleHolder.DEFAULT; } } static final class ComputationTask implements Supplier { @Override - public Scheduler get() throws Exception { + public Scheduler get() { return ComputationHolder.DEFAULT; } } diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java b/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java index 33a5b943e1..33aca58a48 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/TestScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,21 +15,28 @@ import java.util.Queue; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; -import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * A special, non thread-safe scheduler for testing operators that require * a scheduler without introducing real concurrency and allows manually advancing * a virtual time. + *

    + * By default, the tasks submitted via the various {@code schedule} methods are not + * wrapped by the {@link RxJavaPlugins#onSchedule(Runnable)} hook. To enable this behavior, + * create a {@code TestScheduler} via {@link #TestScheduler(boolean)} or {@link #TestScheduler(long, TimeUnit, boolean)}. */ public final class TestScheduler extends Scheduler { /** The ordered queue for the runnable tasks. */ - final Queue queue = new PriorityBlockingQueue(11); + final Queue queue = new PriorityBlockingQueue<>(11); + /** Use the {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. */ + final boolean useOnScheduleHook; /** The per-scheduler global order counter. */ long counter; // Storing time in nanoseconds internally. @@ -39,7 +46,20 @@ public final class TestScheduler extends Scheduler { * Creates a new TestScheduler with initial virtual time of zero. */ public TestScheduler() { - // No-op. + this(false); + } + + /** + * Creates a new TestScheduler with the option to use the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. + *

    History: 3.0.10 - experimental + * @param useOnScheduleHook if {@code true}, the tasks submitted to this + * TestScheduler is wrapped via the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook + * @since 3.1.0 + */ + public TestScheduler(boolean useOnScheduleHook) { + this.useOnScheduleHook = useOnScheduleHook; } /** @@ -51,7 +71,26 @@ public TestScheduler() { * the units of time that {@code delayTime} is expressed in */ public TestScheduler(long delayTime, TimeUnit unit) { + this(delayTime, unit, false); + } + + /** + * Creates a new TestScheduler with the specified initial virtual time + * and with the option to use the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook when scheduling tasks. + *

    History: 3.0.10 - experimental + * @param delayTime + * the point in time to move the Scheduler's clock to + * @param unit + * the units of time that {@code delayTime} is expressed in + * @param useOnScheduleHook if {@code true}, the tasks submitted to this + * TestScheduler is wrapped via the + * {@link RxJavaPlugins#onSchedule(Runnable)} hook + * @since 3.1.0 + */ + public TestScheduler(long delayTime, TimeUnit unit, boolean useOnScheduleHook) { time = unit.toNanos(delayTime); + this.useOnScheduleHook = useOnScheduleHook; } static final class TimedRunnable implements Comparable { @@ -76,9 +115,9 @@ public String toString() { @Override public int compareTo(TimedRunnable o) { if (time == o.time) { - return ObjectHelper.compare(count, o.count); + return Long.compare(count, o.count); } - return ObjectHelper.compare(time, o.time); + return Long.compare(time, o.time); } } @@ -164,10 +203,13 @@ public Disposable schedule(@NonNull Runnable run, long delayTime, @NonNull TimeU if (disposed) { return EmptyDisposable.INSTANCE; } + if (useOnScheduleHook) { + run = RxJavaPlugins.onSchedule(run); + } final TimedRunnable timedAction = new TimedRunnable(this, time + unit.toNanos(delayTime), run, counter++); queue.add(timedAction); - return Disposables.fromRunnable(new QueueRemove(timedAction)); + return new QueueRemove(timedAction); } @NonNull @@ -176,9 +218,12 @@ public Disposable schedule(@NonNull Runnable run) { if (disposed) { return EmptyDisposable.INSTANCE; } + if (useOnScheduleHook) { + run = RxJavaPlugins.onSchedule(run); + } final TimedRunnable timedAction = new TimedRunnable(this, 0, run, counter++); queue.add(timedAction); - return Disposables.fromRunnable(new QueueRemove(timedAction)); + return new QueueRemove(timedAction); } @Override @@ -186,16 +231,25 @@ public long now(@NonNull TimeUnit unit) { return TestScheduler.this.now(unit); } - final class QueueRemove implements Runnable { - final TimedRunnable timedAction; + final class QueueRemove extends AtomicReference implements Disposable { + + private static final long serialVersionUID = -7874968252110604360L; QueueRemove(TimedRunnable timedAction) { - this.timedAction = timedAction; + this.lazySet(timedAction); + } + + @Override + public void dispose() { + TimedRunnable tr = getAndSet(null); + if (tr != null) { + queue.remove(tr); + } } @Override - public void run() { - queue.remove(timedAction); + public boolean isDisposed() { + return get() == null; } } } diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/Timed.java b/src/main/java/io/reactivex/rxjava3/schedulers/Timed.java index b7ac9c828f..3ef1940984 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/Timed.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/Timed.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,10 +13,10 @@ package io.reactivex.rxjava3.schedulers; +import java.util.Objects; import java.util.concurrent.TimeUnit; import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; /** * Holds onto a value along with time information. @@ -29,16 +29,16 @@ public final class Timed { final TimeUnit unit; /** - * Constructs a Timed instance with the given value and time information. + * Constructs a {@code Timed} instance with the given value and time information. * @param value the value to hold * @param time the time to hold * @param unit the time unit, not null - * @throws NullPointerException if unit is null + * @throws NullPointerException if {@code value} or {@code unit} is {@code null} */ public Timed(@NonNull T value, long time, @NonNull TimeUnit unit) { - this.value = value; + this.value = Objects.requireNonNull(value, "value is null"); this.time = time; - this.unit = ObjectHelper.requireNonNull(unit, "unit is null"); + this.unit = Objects.requireNonNull(unit, "unit is null"); } /** @@ -69,7 +69,7 @@ public long time() { /** * Returns the contained time value in the time unit specified. - * @param unit the time unt + * @param unit the time unit * @return the converted time */ public long time(@NonNull TimeUnit unit) { @@ -80,16 +80,16 @@ public long time(@NonNull TimeUnit unit) { public boolean equals(Object other) { if (other instanceof Timed) { Timed o = (Timed) other; - return ObjectHelper.equals(value, o.value) + return Objects.equals(value, o.value) && time == o.time - && ObjectHelper.equals(unit, o.unit); + && Objects.equals(unit, o.unit); } return false; } @Override public int hashCode() { - int h = value != null ? value.hashCode() : 0; + int h = value.hashCode(); h = h * 31 + (int)((time >>> 31) ^ time); h = h * 31 + unit.hashCode(); return h; diff --git a/src/main/java/io/reactivex/rxjava3/schedulers/package-info.java b/src/main/java/io/reactivex/rxjava3/schedulers/package-info.java index 4bbd9e0da2..1f96d0fd2a 100644 --- a/src/main/java/io/reactivex/rxjava3/schedulers/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/schedulers/package-info.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + /** * Contains notably the factory class of {@link io.reactivex.rxjava3.schedulers.Schedulers Schedulers} providing methods for * retrieving the standard scheduler instances, the {@link io.reactivex.rxjava3.schedulers.TestScheduler TestScheduler} for testing flows diff --git a/src/main/java/io/reactivex/rxjava3/subjects/AsyncSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/AsyncSubject.java index 174c98c4f5..dd4957fd39 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/AsyncSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/AsyncSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -129,7 +129,7 @@ public final class AsyncSubject extends Subject { @CheckReturnValue @NonNull public static AsyncSubject create() { - return new AsyncSubject(); + return new AsyncSubject<>(); } /** @@ -138,7 +138,7 @@ public static AsyncSubject create() { */ @SuppressWarnings("unchecked") AsyncSubject() { - this.subscribers = new AtomicReference[]>(EMPTY); + this.subscribers = new AtomicReference<>(EMPTY); } @Override @@ -192,28 +192,32 @@ public void onComplete() { } @Override + @CheckReturnValue public boolean hasObservers() { return subscribers.get().length != 0; } @Override + @CheckReturnValue public boolean hasThrowable() { return subscribers.get() == TERMINATED && error != null; } @Override + @CheckReturnValue public boolean hasComplete() { return subscribers.get() == TERMINATED && error == null; } @Override + @CheckReturnValue public Throwable getThrowable() { return subscribers.get() == TERMINATED ? error : null; } @Override protected void subscribeActual(Observer observer) { - AsyncDisposable as = new AsyncDisposable(observer, this); + AsyncDisposable as = new AsyncDisposable<>(observer, this); observer.onSubscribe(as); if (add(as)) { if (as.isDisposed()) { @@ -304,6 +308,7 @@ void remove(AsyncDisposable ps) { *

    The method is thread-safe. * @return true if the subject has any value */ + @CheckReturnValue public boolean hasValue() { return subscribers.get() == TERMINATED && value != null; } @@ -314,6 +319,7 @@ public boolean hasValue() { * @return a single value the Subject currently has or null if no such value exists */ @Nullable + @CheckReturnValue public T getValue() { return subscribers.get() == TERMINATED ? value : null; } diff --git a/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java index 45ddb72a51..2b19ecdd26 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/BehaviorSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,13 +13,13 @@ package io.reactivex.rxjava3.subjects; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.*; import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.internal.util.AppendOnlyLinkedArrayList.NonThrowingPredicate; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -28,7 +28,7 @@ * Subject that emits the most recent item it has observed and all subsequent observed items to each subscribed * {@link Observer}. *

    - * + * *

    * This subject does not have a public constructor by design; a new empty instance of this * {@code BehaviorSubject} can be created via the {@link #create()} method and @@ -59,9 +59,9 @@ * * TestObserver<Integer> to1 = observable.test(); * - * observable.onNext(1); + * subject.onNext(1); * // this will "clear" the cache - * observable.onNext(EMPTY); + * subject.onNext(EMPTY); * * TestObserver<Integer> to2 = observable.test(); * @@ -151,7 +151,7 @@ public final class BehaviorSubject extends Subject { final AtomicReference value; - final AtomicReference[]> subscribers; + final AtomicReference[]> observers; @SuppressWarnings("rawtypes") static final BehaviorDisposable[] EMPTY = new BehaviorDisposable[0]; @@ -176,7 +176,7 @@ public final class BehaviorSubject extends Subject { @CheckReturnValue @NonNull public static BehaviorSubject create() { - return new BehaviorSubject(); + return new BehaviorSubject<>(null); } /** @@ -189,41 +189,33 @@ public static BehaviorSubject create() { * the item that will be emitted first to any {@link Observer} as long as the * {@link BehaviorSubject} has not yet observed any items from its source {@code Observable} * @return the constructed {@link BehaviorSubject} + * @throws NullPointerException if {@code defaultValue} is {@code null} */ @CheckReturnValue @NonNull - public static BehaviorSubject createDefault(T defaultValue) { - return new BehaviorSubject(defaultValue); + public static <@NonNull T> BehaviorSubject createDefault(T defaultValue) { + Objects.requireNonNull(defaultValue, "defaultValue is null"); + return new BehaviorSubject<>(defaultValue); } /** * Constructs an empty BehaviorSubject. + * @param defaultValue the initial value, not null (verified) * @since 2.0 */ @SuppressWarnings("unchecked") - BehaviorSubject() { + BehaviorSubject(T defaultValue) { this.lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); - this.subscribers = new AtomicReference[]>(EMPTY); - this.value = new AtomicReference(); - this.terminalEvent = new AtomicReference(); - } - - /** - * Constructs a BehaviorSubject with the given initial value. - * @param defaultValue the initial value, not null (verified) - * @throws NullPointerException if {@code defaultValue} is null - * @since 2.0 - */ - BehaviorSubject(T defaultValue) { - this(); - this.value.lazySet(ObjectHelper.requireNonNull(defaultValue, "defaultValue is null")); + this.observers = new AtomicReference<>(EMPTY); + this.value = new AtomicReference<>(defaultValue); + this.terminalEvent = new AtomicReference<>(); } @Override protected void subscribeActual(Observer observer) { - BehaviorDisposable bs = new BehaviorDisposable(observer, this); + BehaviorDisposable bs = new BehaviorDisposable<>(observer, this); observer.onSubscribe(bs); if (add(bs)) { if (bs.cancelled) { @@ -257,7 +249,7 @@ public void onNext(T t) { } Object o = NotificationLite.next(t); setCurrent(o); - for (BehaviorDisposable bs : subscribers.get()) { + for (BehaviorDisposable bs : observers.get()) { bs.emitNext(o, index); } } @@ -287,16 +279,19 @@ public void onComplete() { } @Override + @CheckReturnValue public boolean hasObservers() { - return subscribers.get().length != 0; + return observers.get().length != 0; } + @CheckReturnValue /* test support*/ int subscriberCount() { - return subscribers.get().length; + return observers.get().length; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { Object o = value.get(); if (NotificationLite.isError(o)) { @@ -311,6 +306,7 @@ public Throwable getThrowable() { * @return a single value the Subject currently has or null if no such value exists */ @Nullable + @CheckReturnValue public T getValue() { Object o = value.get(); if (NotificationLite.isComplete(o) || NotificationLite.isError(o)) { @@ -320,12 +316,14 @@ public T getValue() { } @Override + @CheckReturnValue public boolean hasComplete() { Object o = value.get(); return NotificationLite.isComplete(o); } @Override + @CheckReturnValue public boolean hasThrowable() { Object o = value.get(); return NotificationLite.isError(o); @@ -336,6 +334,7 @@ public boolean hasThrowable() { *

    The method is thread-safe. * @return true if the subject has any value */ + @CheckReturnValue public boolean hasValue() { Object o = value.get(); return o != null && !NotificationLite.isComplete(o) && !NotificationLite.isError(o); @@ -343,7 +342,7 @@ public boolean hasValue() { boolean add(BehaviorDisposable rs) { for (;;) { - BehaviorDisposable[] a = subscribers.get(); + BehaviorDisposable[] a = observers.get(); if (a == TERMINATED) { return false; } @@ -352,7 +351,7 @@ boolean add(BehaviorDisposable rs) { BehaviorDisposable[] b = new BehaviorDisposable[len + 1]; System.arraycopy(a, 0, b, 0, len); b[len] = rs; - if (subscribers.compareAndSet(a, b)) { + if (observers.compareAndSet(a, b)) { return true; } } @@ -361,7 +360,7 @@ boolean add(BehaviorDisposable rs) { @SuppressWarnings("unchecked") void remove(BehaviorDisposable rs) { for (;;) { - BehaviorDisposable[] a = subscribers.get(); + BehaviorDisposable[] a = observers.get(); int len = a.length; if (len == 0) { return; @@ -385,7 +384,7 @@ void remove(BehaviorDisposable rs) { System.arraycopy(a, 0, b, 0, j); System.arraycopy(a, j + 1, b, j, len - j - 1); } - if (subscribers.compareAndSet(a, b)) { + if (observers.compareAndSet(a, b)) { return; } } @@ -394,13 +393,9 @@ void remove(BehaviorDisposable rs) { @SuppressWarnings("unchecked") BehaviorDisposable[] terminate(Object terminalValue) { - BehaviorDisposable[] a = subscribers.getAndSet(TERMINATED); - if (a != TERMINATED) { - // either this or atomics with lots of allocation - setCurrent(terminalValue); - } + setCurrent(terminalValue); - return a; + return observers.getAndSet(TERMINATED); } void setCurrent(Object o) { @@ -493,7 +488,7 @@ void emitNext(Object value, long stateIndex) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(value); diff --git a/src/main/java/io/reactivex/rxjava3/subjects/CompletableSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/CompletableSubject.java index bbb4b24b0c..57be7dbced 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/CompletableSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/CompletableSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -107,7 +107,7 @@ public static CompletableSubject create() { CompletableSubject() { once = new AtomicBoolean(); - observers = new AtomicReference(EMPTY); + observers = new AtomicReference<>(EMPTY); } @Override diff --git a/src/main/java/io/reactivex/rxjava3/subjects/MaybeSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/MaybeSubject.java index a2f62524f0..39dbb78d9c 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/MaybeSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/MaybeSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -131,13 +131,13 @@ public final class MaybeSubject extends Maybe implements MaybeObserver @CheckReturnValue @NonNull public static MaybeSubject create() { - return new MaybeSubject(); + return new MaybeSubject<>(); } @SuppressWarnings("unchecked") MaybeSubject() { once = new AtomicBoolean(); - observers = new AtomicReference[]>(EMPTY); + observers = new AtomicReference<>(EMPTY); } @Override @@ -185,7 +185,7 @@ public void onComplete() { @Override protected void subscribeActual(MaybeObserver observer) { - MaybeDisposable md = new MaybeDisposable(observer, this); + MaybeDisposable md = new MaybeDisposable<>(observer, this); observer.onSubscribe(md); if (add(md)) { if (md.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/subjects/PublishSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/PublishSubject.java index a81d6062fd..6c2d5ac033 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/PublishSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/PublishSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -115,7 +115,7 @@ public final class PublishSubject extends Subject { @CheckReturnValue @NonNull public static PublishSubject create() { - return new PublishSubject(); + return new PublishSubject<>(); } /** @@ -124,12 +124,12 @@ public static PublishSubject create() { */ @SuppressWarnings("unchecked") PublishSubject() { - subscribers = new AtomicReference[]>(EMPTY); + subscribers = new AtomicReference<>(EMPTY); } @Override protected void subscribeActual(Observer t) { - PublishDisposable ps = new PublishDisposable(t, this); + PublishDisposable ps = new PublishDisposable<>(t, this); t.onSubscribe(ps); if (add(ps)) { // if cancellation happened while a successful add, the remove() didn't work @@ -254,12 +254,14 @@ public void onComplete() { } @Override + @CheckReturnValue public boolean hasObservers() { return subscribers.get().length != 0; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { if (subscribers.get() == TERMINATED) { return error; @@ -268,11 +270,13 @@ public Throwable getThrowable() { } @Override + @CheckReturnValue public boolean hasThrowable() { return subscribers.get() == TERMINATED && error != null; } @Override + @CheckReturnValue public boolean hasComplete() { return subscribers.get() == TERMINATED && error == null; } diff --git a/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java b/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java index 2d9dbe779a..e103594ba5 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/ReplaySubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -160,7 +160,7 @@ public final class ReplaySubject extends Subject { @CheckReturnValue @NonNull public static ReplaySubject create() { - return new ReplaySubject(new UnboundedReplayBuffer(16)); + return new ReplaySubject<>(new UnboundedReplayBuffer<>(16)); } /** @@ -177,11 +177,13 @@ public static ReplaySubject create() { * @param capacityHint * the initial buffer capacity * @return the created subject + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull public static ReplaySubject create(int capacityHint) { - return new ReplaySubject(new UnboundedReplayBuffer(capacityHint)); + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + return new ReplaySubject<>(new UnboundedReplayBuffer<>(capacityHint)); } /** @@ -203,11 +205,13 @@ public static ReplaySubject create(int capacityHint) { * @param maxSize * the maximum number of buffered items * @return the created subject + * @throws IllegalArgumentException if {@code maxSize} is non-positive */ @CheckReturnValue @NonNull public static ReplaySubject createWithSize(int maxSize) { - return new ReplaySubject(new SizeBoundReplayBuffer(maxSize)); + ObjectHelper.verifyPositive(maxSize, "maxSize"); + return new ReplaySubject<>(new SizeBoundReplayBuffer<>(maxSize)); } /** @@ -224,7 +228,7 @@ public static ReplaySubject createWithSize(int maxSize) { * @return the created subject */ /* test */ static ReplaySubject createUnbounded() { - return new ReplaySubject(new SizeBoundReplayBuffer(Integer.MAX_VALUE)); + return new ReplaySubject<>(new SizeBoundReplayBuffer<>(Integer.MAX_VALUE)); } /** @@ -258,11 +262,16 @@ public static ReplaySubject createWithSize(int maxSize) { * @param scheduler * the {@link Scheduler} that provides the current time * @return the created subject + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code maxAge} is non-positive */ @CheckReturnValue @NonNull - public static ReplaySubject createWithTime(long maxAge, TimeUnit unit, Scheduler scheduler) { - return new ReplaySubject(new SizeAndTimeBoundReplayBuffer(Integer.MAX_VALUE, maxAge, unit, scheduler)); + public static ReplaySubject createWithTime(long maxAge, @NonNull TimeUnit unit, @NonNull Scheduler scheduler) { + ObjectHelper.verifyPositive(maxAge, "maxAge"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return new ReplaySubject<>(new SizeAndTimeBoundReplayBuffer<>(Integer.MAX_VALUE, maxAge, unit, scheduler)); } /** @@ -298,11 +307,17 @@ public static ReplaySubject createWithTime(long maxAge, TimeUnit unit, Sc * @param scheduler * the {@link Scheduler} that provides the current time * @return the created subject + * @throws NullPointerException if {@code unit} or {@code scheduler} is {@code null} + * @throws IllegalArgumentException if {@code maxAge} or {@code maxSize} is non-positive */ @CheckReturnValue @NonNull - public static ReplaySubject createWithTimeAndSize(long maxAge, TimeUnit unit, Scheduler scheduler, int maxSize) { - return new ReplaySubject(new SizeAndTimeBoundReplayBuffer(maxSize, maxAge, unit, scheduler)); + public static ReplaySubject createWithTimeAndSize(long maxAge, @NonNull TimeUnit unit, @NonNull Scheduler scheduler, int maxSize) { + ObjectHelper.verifyPositive(maxSize, "maxSize"); + ObjectHelper.verifyPositive(maxAge, "maxAge"); + Objects.requireNonNull(unit, "unit is null"); + Objects.requireNonNull(scheduler, "scheduler is null"); + return new ReplaySubject<>(new SizeAndTimeBoundReplayBuffer<>(maxSize, maxAge, unit, scheduler)); } /** @@ -312,23 +327,21 @@ public static ReplaySubject createWithTimeAndSize(long maxAge, TimeUnit u @SuppressWarnings("unchecked") ReplaySubject(ReplayBuffer buffer) { this.buffer = buffer; - this.observers = new AtomicReference[]>(EMPTY); + this.observers = new AtomicReference<>(EMPTY); } @Override protected void subscribeActual(Observer observer) { - ReplayDisposable rs = new ReplayDisposable(observer, this); + ReplayDisposable rs = new ReplayDisposable<>(observer, this); observer.onSubscribe(rs); - if (!rs.cancelled) { - if (add(rs)) { - if (rs.cancelled) { - remove(rs); - return; - } + if (add(rs)) { + if (rs.cancelled) { + remove(rs); + return; } - buffer.replay(rs); } + buffer.replay(rs); } @Override @@ -392,16 +405,19 @@ public void onComplete() { } @Override + @CheckReturnValue public boolean hasObservers() { return observers.get().length != 0; } + @CheckReturnValue /* test */ int observerCount() { return observers.get().length; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { Object o = buffer.get(); if (NotificationLite.isError(o)) { @@ -416,6 +432,7 @@ public Throwable getThrowable() { * @return a single value the Subject currently has or null if no such value exists */ @Nullable + @CheckReturnValue public T getValue() { return buffer.getValue(); } @@ -446,6 +463,7 @@ public void cleanupBuffer() { *

    The method is thread-safe. * @return the array containing the snapshot of all values of the Subject */ + @CheckReturnValue public Object[] getValues() { @SuppressWarnings("unchecked") T[] a = (T[])EMPTY_ARRAY; @@ -465,17 +483,20 @@ public Object[] getValues() { * @param array the target array to copy values into if it fits * @return the given array if the values fit into it or a new array containing all values */ + @CheckReturnValue public T[] getValues(T[] array) { return buffer.getValues(array); } @Override + @CheckReturnValue public boolean hasComplete() { Object o = buffer.get(); return NotificationLite.isComplete(o); } @Override + @CheckReturnValue public boolean hasThrowable() { Object o = buffer.get(); return NotificationLite.isError(o); @@ -486,10 +507,12 @@ public boolean hasThrowable() { *

    The method is thread-safe. * @return true if the subject has any value */ + @CheckReturnValue public boolean hasValue() { return buffer.size() != 0; // NOPMD } + @CheckReturnValue /* test*/ int size() { return buffer.size(); } @@ -546,10 +569,8 @@ void remove(ReplayDisposable rs) { @SuppressWarnings("unchecked") ReplayDisposable[] terminate(Object terminalValue) { - if (buffer.compareAndSet(null, terminalValue)) { - return observers.getAndSet(TERMINATED); - } - return TERMINATED; + buffer.compareAndSet(null, terminalValue); + return observers.getAndSet(TERMINATED); } /** @@ -631,12 +652,10 @@ static final class UnboundedReplayBuffer final List buffer; - volatile boolean done; - volatile int size; UnboundedReplayBuffer(int capacityHint) { - this.buffer = new ArrayList(ObjectHelper.verifyPositive(capacityHint, "capacityHint")); + this.buffer = new ArrayList<>(capacityHint); } @Override @@ -650,7 +669,6 @@ public void addFinal(Object notificationLite) { buffer.add(notificationLite); trimHead(); size++; - done = true; } @Override @@ -751,20 +769,17 @@ public void replay(ReplayDisposable rs) { Object o = b.get(index); - if (done) { - if (index + 1 == s) { - s = size; - if (index + 1 == s) { - if (NotificationLite.isComplete(o)) { - a.onComplete(); - } else { - a.onError(NotificationLite.getError(o)); - } - rs.index = null; - rs.cancelled = true; - return; - } - } + if (NotificationLite.isComplete(o)) { + a.onComplete(); + rs.index = null; + rs.cancelled = true; + return; + } else + if (NotificationLite.isError(o)) { + a.onError(NotificationLite.getError(o)); + rs.index = null; + rs.cancelled = true; + return; } a.onNext((T)o); @@ -835,11 +850,9 @@ static final class SizeBoundReplayBuffer Node tail; - volatile boolean done; - SizeBoundReplayBuffer(int maxSize) { - this.maxSize = ObjectHelper.verifyPositive(maxSize, "maxSize"); - Node h = new Node(null); + this.maxSize = maxSize; + Node h = new Node<>(null); this.tail = h; this.head = h; } @@ -854,7 +867,7 @@ void trim() { @Override public void add(T value) { - Node n = new Node(value); + Node n = new Node<>(value); Node t = tail; tail = n; @@ -866,7 +879,7 @@ public void add(T value) { @Override public void addFinal(Object notificationLite) { - Node n = new Node(notificationLite); + Node n = new Node<>(notificationLite); Node t = tail; tail = n; @@ -874,7 +887,6 @@ public void addFinal(Object notificationLite) { t.lazySet(n); // releases both the tail and size trimHead(); - done = true; } /** @@ -885,7 +897,7 @@ public void addFinal(Object notificationLite) { public void trimHead() { Node h = head; if (h.value != null) { - Node n = new Node(null); + Node n = new Node<>(null); n.lazySet(h.get()); head = n; } @@ -979,18 +991,17 @@ public void replay(ReplayDisposable rs) { Object o = n.value; - if (done) { - if (n.get() == null) { - - if (NotificationLite.isComplete(o)) { - a.onComplete(); - } else { - a.onError(NotificationLite.getError(o)); - } - rs.index = null; - rs.cancelled = true; - return; - } + if (NotificationLite.isComplete(o)) { + a.onComplete(); + rs.index = null; + rs.cancelled = true; + return; + } else + if (NotificationLite.isError(o)) { + a.onError(NotificationLite.getError(o)); + rs.index = null; + rs.cancelled = true; + return; } a.onNext((T)o); @@ -1048,14 +1059,12 @@ static final class SizeAndTimeBoundReplayBuffer TimedNode tail; - volatile boolean done; - SizeAndTimeBoundReplayBuffer(int maxSize, long maxAge, TimeUnit unit, Scheduler scheduler) { - this.maxSize = ObjectHelper.verifyPositive(maxSize, "maxSize"); - this.maxAge = ObjectHelper.verifyPositive(maxAge, "maxAge"); - this.unit = ObjectHelper.requireNonNull(unit, "unit is null"); - this.scheduler = ObjectHelper.requireNonNull(scheduler, "scheduler is null"); - TimedNode h = new TimedNode(null, 0L); + this.maxSize = maxSize; + this.maxAge = maxAge; + this.unit = unit; + this.scheduler = scheduler; + TimedNode h = new TimedNode<>(null, 0L); this.tail = h; this.head = h; } @@ -1076,10 +1085,6 @@ void trim() { break; } TimedNode next = h.get(); - if (next == null) { - head = h; - break; - } if (next.time > limit) { head = h; @@ -1101,7 +1106,7 @@ void trimFinal() { TimedNode next = h.get(); if (next.get() == null) { if (h.value != null) { - TimedNode lasth = new TimedNode(null, 0L); + TimedNode lasth = new TimedNode<>(null, 0L); lasth.lazySet(h.get()); head = lasth; } else { @@ -1112,7 +1117,7 @@ void trimFinal() { if (next.time > limit) { if (h.value != null) { - TimedNode lasth = new TimedNode(null, 0L); + TimedNode lasth = new TimedNode<>(null, 0L); lasth.lazySet(h.get()); head = lasth; } else { @@ -1127,7 +1132,7 @@ void trimFinal() { @Override public void add(T value) { - TimedNode n = new TimedNode(value, scheduler.now(unit)); + TimedNode n = new TimedNode<>(value, scheduler.now(unit)); TimedNode t = tail; tail = n; @@ -1139,15 +1144,13 @@ public void add(T value) { @Override public void addFinal(Object notificationLite) { - TimedNode n = new TimedNode(notificationLite, Long.MAX_VALUE); + TimedNode n = new TimedNode<>(notificationLite, Long.MAX_VALUE); TimedNode t = tail; tail = n; size++; t.lazySet(n); // releases both the tail and size trimFinal(); - - done = true; } /** @@ -1158,7 +1161,7 @@ public void addFinal(Object notificationLite) { public void trimHead() { TimedNode h = head; if (h.value != null) { - TimedNode n = new TimedNode(null, 0); + TimedNode n = new TimedNode<>(null, 0); n.lazySet(h.get()); head = n; } @@ -1259,11 +1262,6 @@ public void replay(ReplayDisposable rs) { for (;;) { - if (rs.cancelled) { - rs.index = null; - return; - } - for (;;) { if (rs.cancelled) { rs.index = null; @@ -1278,18 +1276,17 @@ public void replay(ReplayDisposable rs) { Object o = n.value; - if (done) { - if (n.get() == null) { - - if (NotificationLite.isComplete(o)) { - a.onComplete(); - } else { - a.onError(NotificationLite.getError(o)); - } - rs.index = null; - rs.cancelled = true; - return; - } + if (NotificationLite.isComplete(o)) { + a.onComplete(); + rs.index = null; + rs.cancelled = true; + return; + } else + if (NotificationLite.isError(o)) { + a.onError(NotificationLite.getError(o)); + rs.index = null; + rs.cancelled = true; + return; } a.onNext((T)o); @@ -1297,10 +1294,6 @@ public void replay(ReplayDisposable rs) { index = n; } - if (index.get() != null) { - continue; - } - rs.index = index; missed = rs.addAndGet(-missed); diff --git a/src/main/java/io/reactivex/rxjava3/subjects/SerializedSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/SerializedSubject.java index d70cf2f15c..09a45b1da2 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/SerializedSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/SerializedSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -60,7 +60,7 @@ public void onSubscribe(Disposable d) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(NotificationLite.disposable(d)); @@ -93,7 +93,7 @@ public void onNext(T t) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(NotificationLite.next(t)); @@ -120,7 +120,7 @@ public void onError(Throwable t) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.setFirst(NotificationLite.error(t)); @@ -150,7 +150,7 @@ public void onComplete() { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(4); + q = new AppendOnlyLinkedArrayList<>(4); queue = q; } q.add(NotificationLite.complete()); diff --git a/src/main/java/io/reactivex/rxjava3/subjects/SingleSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/SingleSubject.java index fdd1c806f6..7559f0329d 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/SingleSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/SingleSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -115,13 +115,13 @@ public final class SingleSubject extends Single implements SingleObserver< @CheckReturnValue @NonNull public static SingleSubject create() { - return new SingleSubject(); + return new SingleSubject<>(); } @SuppressWarnings("unchecked") SingleSubject() { once = new AtomicBoolean(); - observers = new AtomicReference[]>(EMPTY); + observers = new AtomicReference<>(EMPTY); } @Override @@ -159,7 +159,7 @@ public void onError(@NonNull Throwable e) { @Override protected void subscribeActual(@NonNull SingleObserver observer) { - SingleDisposable md = new SingleDisposable(observer, this); + SingleDisposable md = new SingleDisposable<>(observer, this); observer.onSubscribe(md); if (add(md)) { if (md.isDisposed()) { diff --git a/src/main/java/io/reactivex/rxjava3/subjects/Subject.java b/src/main/java/io/reactivex/rxjava3/subjects/Subject.java index 6a3b6462be..b76a15da33 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/Subject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/Subject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,6 +32,7 @@ public abstract class Subject extends Observable implements Observer { *

    The method is thread-safe. * @return true if the subject has any Observers */ + @CheckReturnValue public abstract boolean hasObservers(); /** @@ -41,6 +42,7 @@ public abstract class Subject extends Observable implements Observer { * @see #getThrowable() * @see #hasComplete() */ + @CheckReturnValue public abstract boolean hasThrowable(); /** @@ -49,6 +51,7 @@ public abstract class Subject extends Observable implements Observer { * @return true if the subject has reached a terminal state through a complete event * @see #hasThrowable() */ + @CheckReturnValue public abstract boolean hasComplete(); /** @@ -59,6 +62,7 @@ public abstract class Subject extends Observable implements Observer { * hasn't terminated yet */ @Nullable + @CheckReturnValue public abstract Throwable getThrowable(); /** @@ -68,10 +72,11 @@ public abstract class Subject extends Observable implements Observer { * @return the wrapped and serialized subject */ @NonNull + @CheckReturnValue public final Subject toSerialized() { if (this instanceof SerializedSubject) { return this; } - return new SerializedSubject(this); + return new SerializedSubject<>(this); } } diff --git a/src/main/java/io/reactivex/rxjava3/subjects/UnicastSubject.java b/src/main/java/io/reactivex/rxjava3/subjects/UnicastSubject.java index be824a7708..3092dc73c0 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/UnicastSubject.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/UnicastSubject.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,23 +13,24 @@ package io.reactivex.rxjava3.subjects; +import java.util.Objects; import java.util.concurrent.atomic.*; import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.SimpleQueue; +import io.reactivex.rxjava3.internal.functions.*; import io.reactivex.rxjava3.internal.observers.BasicIntQueueDisposable; -import io.reactivex.rxjava3.internal.queue.SpscLinkedArrayQueue; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.operators.SpscLinkedArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** * A Subject that queues up events until a single {@link Observer} subscribes to it, replays * those events to it until the {@code Observer} catches up and then switches to relaying events live to - * this single {@code Observer} until this {@code UnicastSubject} terminates or the {@code Observer} unsubscribes. + * this single {@code Observer} until this {@code UnicastSubject} terminates or the {@code Observer} disposes. *

    * *

    @@ -47,10 +48,10 @@ * optionally delays an error it receives and replays it after the regular items have been emitted. *

  • {@link #create(int, Runnable)} - creates an empty, unbounded {@code UnicastSubject} * with a hint about how many total items one expects to retain and a callback that will be - * called exactly once when the {@code UnicastSubject} gets terminated or the single {@code Observer} unsubscribes.
  • + * called exactly once when the {@code UnicastSubject} gets terminated or the single {@code Observer} disposes. *
  • {@link #create(int, Runnable, boolean)} - creates an empty, unbounded {@code UnicastSubject} * with a hint about how many total items one expects to retain and a callback that will be - * called exactly once when the {@code UnicastSubject} gets terminated or the single {@code Observer} unsubscribes + * called exactly once when the {@code UnicastSubject} gets terminated or the single {@code Observer} disposes * and optionally delays an error it receives and replays it after the regular items have been emitted.
  • * *

    @@ -179,7 +180,7 @@ public final class UnicastSubject extends Subject { @CheckReturnValue @NonNull public static UnicastSubject create() { - return new UnicastSubject(bufferSize(), true); + return new UnicastSubject<>(bufferSize(), null, true); } /** @@ -187,16 +188,19 @@ public static UnicastSubject create() { * @param the value type * @param capacityHint the hint to size the internal unbounded buffer * @return an UnicastSubject instance + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull public static UnicastSubject create(int capacityHint) { - return new UnicastSubject(capacityHint, true); + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + return new UnicastSubject<>(capacityHint, null, true); } /** * Creates an UnicastSubject with the given internal buffer capacity hint and a callback for - * the case when the single Subscriber cancels its subscription. + * the case when the single Subscriber cancels its subscription + * or the subject is terminated. * *

    The callback, if not null, is called exactly once and * non-overlapped with any active replay. @@ -205,16 +209,21 @@ public static UnicastSubject create(int capacityHint) { * @param capacityHint the hint to size the internal unbounded buffer * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed * @return an UnicastSubject instance + * @throws NullPointerException if {@code onTerminate} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive */ @CheckReturnValue @NonNull - public static UnicastSubject create(int capacityHint, Runnable onTerminate) { - return new UnicastSubject(capacityHint, onTerminate, true); + public static UnicastSubject create(int capacityHint, @NonNull Runnable onTerminate) { + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + Objects.requireNonNull(onTerminate, "onTerminate"); + return new UnicastSubject<>(capacityHint, onTerminate, true); } /** * Creates an UnicastSubject with the given internal buffer capacity hint, delay error flag and - * a callback for the case when the single Subscriber cancels its subscription. + * a callback for the case when the single Observer disposes its {@link Disposable} + * or the subject is terminated. * *

    The callback, if not null, is called exactly once and * non-overlapped with any active replay. @@ -224,12 +233,16 @@ public static UnicastSubject create(int capacityHint, Runnable onTerminat * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed * @param delayError deliver pending onNext events before onError * @return an UnicastSubject instance + * @throws NullPointerException if {@code onTerminate} is {@code null} + * @throws IllegalArgumentException if {@code capacityHint} is non-positive * @since 2.2 */ @CheckReturnValue @NonNull - public static UnicastSubject create(int capacityHint, Runnable onTerminate, boolean delayError) { - return new UnicastSubject(capacityHint, onTerminate, delayError); + public static UnicastSubject create(int capacityHint, @NonNull Runnable onTerminate, boolean delayError) { + ObjectHelper.verifyPositive(capacityHint, "capacityHint"); + Objects.requireNonNull(onTerminate, "onTerminate"); + return new UnicastSubject<>(capacityHint, onTerminate, delayError); } /** @@ -246,35 +259,7 @@ public static UnicastSubject create(int capacityHint, Runnable onTerminat @CheckReturnValue @NonNull public static UnicastSubject create(boolean delayError) { - return new UnicastSubject(bufferSize(), delayError); - } - - /** - * Creates an UnicastSubject with the given capacity hint and delay error flag. - *

    History: 2.0.8 - experimental - * @param capacityHint the capacity hint for the internal, unbounded queue - * @param delayError deliver pending onNext events before onError - * @since 2.2 - */ - UnicastSubject(int capacityHint, boolean delayError) { - this.queue = new SpscLinkedArrayQueue(ObjectHelper.verifyPositive(capacityHint, "capacityHint")); - this.onTerminate = new AtomicReference(); - this.delayError = delayError; - this.downstream = new AtomicReference>(); - this.once = new AtomicBoolean(); - this.wip = new UnicastQueueDisposable(); - } - - /** - * Creates an UnicastSubject with the given capacity hint and callback - * for when the Subject is terminated normally or its single Subscriber cancels. - * @param capacityHint the capacity hint for the internal, unbounded queue - * @param onTerminate the callback to run when the Subject is terminated or cancelled, null not allowed - * @since 2.0 - * - * */ - UnicastSubject(int capacityHint, Runnable onTerminate) { - this(capacityHint, onTerminate, true); + return new UnicastSubject<>(bufferSize(), null, delayError); } /** @@ -287,10 +272,10 @@ public static UnicastSubject create(boolean delayError) { * @since 2.2 */ UnicastSubject(int capacityHint, Runnable onTerminate, boolean delayError) { - this.queue = new SpscLinkedArrayQueue(ObjectHelper.verifyPositive(capacityHint, "capacityHint")); - this.onTerminate = new AtomicReference(ObjectHelper.requireNonNull(onTerminate, "onTerminate")); + this.queue = new SpscLinkedArrayQueue<>(capacityHint); + this.onTerminate = new AtomicReference<>(onTerminate); this.delayError = delayError; - this.downstream = new AtomicReference>(); + this.downstream = new AtomicReference<>(); this.once = new AtomicBoolean(); this.wip = new UnicastQueueDisposable(); } @@ -493,12 +478,14 @@ void drain() { } @Override + @CheckReturnValue public boolean hasObservers() { return downstream.get() != null; } @Override @Nullable + @CheckReturnValue public Throwable getThrowable() { if (done) { return error; @@ -507,11 +494,13 @@ public Throwable getThrowable() { } @Override + @CheckReturnValue public boolean hasThrowable() { return done && error != null; } @Override + @CheckReturnValue public boolean hasComplete() { return done && error == null; } @@ -531,7 +520,7 @@ public int requestFusion(int mode) { @Nullable @Override - public T poll() throws Exception { + public T poll() { return queue.poll(); } diff --git a/src/main/java/io/reactivex/rxjava3/subjects/package-info.java b/src/main/java/io/reactivex/rxjava3/subjects/package-info.java index 2980d499bb..a2736d7eee 100644 --- a/src/main/java/io/reactivex/rxjava3/subjects/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/subjects/package-info.java @@ -1,27 +1,25 @@ -/** +/* * 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. */ /** - * Classes representing so-called hot sources, aka subjects, that implement a base reactive class and + * Classes representing so-called hot sources, aka subjects, that implement a base reactive class and * the respective consumer type at once to allow forms of multicasting events to multiple * consumers as well as consuming another base reactive type of their kind. *

    * Available subject classes with their respective base classes and consumer interfaces: *
    - * + *
    + * * * *
    The available subject classes with their respective base classes and consumer interfaces.
    Subject typeBase classConsumer interface
    {@link io.reactivex.rxjava3.subjects.Subject Subject} diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/DefaultSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/DefaultSubscriber.java index 3e674a16b0..9f8385b586 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/DefaultSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/DefaultSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ * *

    All pre-implemented final methods are thread-safe. * - *

    The default {@link #onStart()} requests Long.MAX_VALUE by default. Override + *

    The default {@link #onStart()} requests {@link Long#MAX_VALUE} by default. Override * the method to request a custom positive amount. * *

    Note that calling {@link #request(long)} from {@link #onStart()} may trigger @@ -85,7 +85,7 @@ public final void onSubscribe(Subscription s) { } /** - * Requests from the upstream Subscription. + * Requests from the upstream {@link Subscription}. * @param n the request amount, positive */ protected final void request(long n) { @@ -96,7 +96,7 @@ protected final void request(long n) { } /** - * Cancels the upstream's Subscription. + * Cancels the upstream's {@link Subscription}. */ protected final void cancel() { Subscription s = this.upstream; diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/DisposableSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/DisposableSubscriber.java index 2c6c126f8d..a321283018 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/DisposableSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/DisposableSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,11 +23,11 @@ import io.reactivex.rxjava3.internal.util.EndConsumerHelper; /** - * An abstract Subscriber that allows asynchronous, external cancellation by implementing Disposable. + * An abstract Subscriber that allows asynchronous, external cancellation by implementing {@link Disposable}. * *

    All pre-implemented final methods are thread-safe. * - *

    The default {@link #onStart()} requests Long.MAX_VALUE by default. Override + *

    The default {@link #onStart()} requests {@link Long#MAX_VALUE} by default. Override * the method to request a custom positive amount. Use the protected {@link #request(long)} * to request more items and {@link #cancel()} to cancel the sequence from within an * {@code onNext} implementation. @@ -74,7 +74,7 @@ * @param the received value type. */ public abstract class DisposableSubscriber implements FlowableSubscriber, Disposable { - final AtomicReference upstream = new AtomicReference(); + final AtomicReference upstream = new AtomicReference<>(); @Override public final void onSubscribe(Subscription s) { @@ -84,18 +84,18 @@ public final void onSubscribe(Subscription s) { } /** - * Called once the single upstream Subscription is set via onSubscribe. + * Called once the single upstream {@link Subscription} is set via {@link #onSubscribe(Subscription)}. */ protected void onStart() { upstream.get().request(Long.MAX_VALUE); } /** - * Requests the specified amount from the upstream if its Subscription is set via + * Requests the specified amount from the upstream if its {@link Subscription} is set via * onSubscribe already. - *

    Note that calling this method before a Subscription is set via onSubscribe - * leads to NullPointerException and meant to be called from inside onStart or - * onNext. + *

    Note that calling this method before a {@link Subscription} is set via {@link #onSubscribe(Subscription)} + * leads to {@link NullPointerException} and meant to be called from inside {@link #onStart()} or + * {@link #onNext(Object)}. * @param n the request amount, positive */ protected final void request(long n) { @@ -103,8 +103,8 @@ protected final void request(long n) { } /** - * Cancels the Subscription set via onSubscribe or makes sure a - * Subscription set asynchronously (later) is cancelled immediately. + * Cancels the Subscription set via {@link #onSubscribe(Subscription)} or makes sure a + * {@link Subscription} set asynchronously (later) is cancelled immediately. *

    This method is thread-safe and can be exposed as a public API. */ protected final void cancel() { diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/ResourceSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/ResourceSubscriber.java index 71bdeb0121..95dccfd2b1 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/ResourceSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/ResourceSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,7 @@ package io.reactivex.rxjava3.subscribers; +import java.util.Objects; import java.util.concurrent.atomic.*; import org.reactivestreams.Subscription; @@ -20,7 +21,6 @@ import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.ListCompositeDisposable; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.EndConsumerHelper; @@ -40,7 +40,7 @@ * {@code ResourceSubscriber} and then add/remove resources to/from the {@code CompositeDisposable} * freely. * - *

    The default {@link #onStart()} requests Long.MAX_VALUE by default. Override + *

    The default {@link #onStart()} requests {@link Long#MAX_VALUE} by default. Override * the method to request a custom positive amount. Use the protected {@link #request(long)} * to request more items and {@link #dispose()} to cancel the sequence from within an * {@code onNext} implementation. @@ -94,7 +94,7 @@ */ public abstract class ResourceSubscriber implements FlowableSubscriber, Disposable { /** The active subscription. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); /** The resource composite, can never be null. */ private final ListCompositeDisposable resources = new ListCompositeDisposable(); @@ -103,14 +103,14 @@ public abstract class ResourceSubscriber implements FlowableSubscriber, Di private final AtomicLong missedRequested = new AtomicLong(); /** - * Adds a resource to this AsyncObserver. + * Adds a resource to this {@code ResourceSubscriber}. * * @param resource the resource to add * - * @throws NullPointerException if resource is null + * @throws NullPointerException if {@code resource} is {@code null} */ public final void add(Disposable resource) { - ObjectHelper.requireNonNull(resource, "resource is null"); + Objects.requireNonNull(resource, "resource is null"); resources.add(resource); } @@ -126,10 +126,10 @@ public final void onSubscribe(Subscription s) { } /** - * Called once the upstream sets a Subscription on this AsyncObserver. + * Called once the upstream sets a {@link Subscription} on this {@code ResourceSubscriber}. * *

    You can perform initialization at this moment. The default - * implementation requests Long.MAX_VALUE from upstream. + * implementation requests {@link Long#MAX_VALUE} from upstream. */ protected void onStart() { request(Long.MAX_VALUE); @@ -138,7 +138,7 @@ protected void onStart() { /** * Request the specified amount of elements from upstream. * - *

    This method can be called before the upstream calls onSubscribe(). + *

    This method can be called before the upstream calls {@link #onSubscribe(Subscription)}. * When the subscription happens, all missed requests are requested. * * @param n the request amount, must be positive @@ -149,10 +149,10 @@ protected final void request(long n) { /** * Cancels the subscription (if any) and disposes the resources associated with - * this AsyncObserver (if any). + * this {@code ResourceSubscriber} (if any). * - *

    This method can be called before the upstream calls onSubscribe at which - * case the Subscription will be immediately cancelled. + *

    This method can be called before the upstream calls {@link #onSubscribe(Subscription)} at which + * case the {@link Subscription} will be immediately cancelled. */ @Override public final void dispose() { @@ -162,8 +162,8 @@ public final void dispose() { } /** - * Returns true if this AsyncObserver has been disposed/cancelled. - * @return true if this AsyncObserver has been disposed/cancelled + * Returns true if this {@code ResourceSubscriber} has been disposed/cancelled. + * @return true if this {@code ResourceSubscriber} has been disposed/cancelled */ @Override public final boolean isDisposed() { diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/SafeSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/SafeSubscriber.java index 096a99e623..f66c34b9b9 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/SafeSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/SafeSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,10 +10,12 @@ * 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 io.reactivex.rxjava3.subscribers; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.subscriptions.*; @@ -21,12 +23,12 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Wraps another Subscriber and ensures all onXXX methods conform the protocol + * Wraps another {@link Subscriber} and ensures all {@code onXXX} methods conform the protocol * (except the requirement for serialized access). * * @param the value type */ -public final class SafeSubscriber implements FlowableSubscriber, Subscription { +public final class SafeSubscriber<@NonNull T> implements FlowableSubscriber, Subscription { /** The actual Subscriber. */ final Subscriber downstream; /** The subscription. */ @@ -35,15 +37,15 @@ public final class SafeSubscriber implements FlowableSubscriber, Subscript boolean done; /** - * Constructs a SafeSubscriber by wrapping the given actual Subscriber. - * @param downstream the actual Subscriber to wrap, not null (not validated) + * Constructs a {@code SafeSubscriber} by wrapping the given actual {@link Subscriber}. + * @param downstream the actual {@code Subscriber} to wrap, not {@code null} (not validated) */ - public SafeSubscriber(Subscriber downstream) { + public SafeSubscriber(@NonNull Subscriber downstream) { this.downstream = downstream; } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { if (SubscriptionHelper.validate(this.upstream, s)) { this.upstream = s; try { @@ -65,7 +67,7 @@ public void onSubscribe(Subscription s) { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { if (done) { return; } @@ -124,7 +126,7 @@ void onNextNoSubscription() { } @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { if (done) { RxJavaPlugins.onError(t); return; diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/SerializedSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/SerializedSubscriber.java index 92ecbbe6d0..1ea8b4021a 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/SerializedSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/SerializedSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,23 +10,26 @@ * 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 io.reactivex.rxjava3.subscribers; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; /** - * Serializes access to the onNext, onError and onComplete methods of another Subscriber. + * Serializes access to the {@link Subscriber#onNext(Object)}, {@link Subscriber#onError(Throwable)} and + * {@link Subscriber#onComplete()} methods of another {@link Subscriber}. * *

    Note that {@link #onSubscribe(Subscription)} is not serialized in respect of the other methods so - * make sure the {@code onSubscribe} is called with a non-null {@code Subscription} + * make sure the {@code onSubscribe} is called with a non-{@code null} {@link Subscription} * before any of the other methods are called. * - *

    The implementation assumes that the actual Subscriber's methods don't throw. + *

    The implementation assumes that the actual {@code Subscriber}'s methods don't throw. * * @param the value type */ @@ -44,27 +47,27 @@ public final class SerializedSubscriber implements FlowableSubscriber, Sub volatile boolean done; /** - * Construct a SerializedSubscriber by wrapping the given actual Subscriber. - * @param downstream the actual Subscriber, not null (not verified) + * Construct a {@code SerializedSubscriber} by wrapping the given actual {@link Subscriber}. + * @param downstream the actual {@code Subscriber}, not null (not verified) */ public SerializedSubscriber(Subscriber downstream) { this(downstream, false); } /** - * Construct a SerializedSubscriber by wrapping the given actual Observer and + * Construct a {@code SerializedSubscriber} by wrapping the given actual {@link Subscriber} and * optionally delaying the errors till all regular values have been emitted * from the internal buffer. - * @param actual the actual Subscriber, not null (not verified) - * @param delayError if true, errors are emitted after regular values have been emitted + * @param actual the actual {@code Subscriber}, not {@code null} (not verified) + * @param delayError if {@code true}, errors are emitted after regular values have been emitted */ - public SerializedSubscriber(Subscriber actual, boolean delayError) { + public SerializedSubscriber(@NonNull Subscriber actual, boolean delayError) { this.downstream = actual; this.delayError = delayError; } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { if (SubscriptionHelper.validate(this.upstream, s)) { this.upstream = s; downstream.onSubscribe(this); @@ -72,7 +75,7 @@ public void onSubscribe(Subscription s) { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { if (done) { return; } @@ -88,7 +91,7 @@ public void onNext(T t) { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(QUEUE_LINK_SIZE); + q = new AppendOnlyLinkedArrayList<>(QUEUE_LINK_SIZE); queue = q; } q.add(NotificationLite.next(t)); @@ -117,7 +120,7 @@ public void onError(Throwable t) { done = true; AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(QUEUE_LINK_SIZE); + q = new AppendOnlyLinkedArrayList<>(QUEUE_LINK_SIZE); queue = q; } Object err = NotificationLite.error(t); @@ -155,7 +158,7 @@ public void onComplete() { if (emitting) { AppendOnlyLinkedArrayList q = queue; if (q == null) { - q = new AppendOnlyLinkedArrayList(QUEUE_LINK_SIZE); + q = new AppendOnlyLinkedArrayList<>(QUEUE_LINK_SIZE); queue = q; } q.add(NotificationLite.complete()); diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java b/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java index 3607337b36..1d96e7cfd3 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/TestSubscriber.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,26 +10,26 @@ * 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 io.reactivex.rxjava3.subscribers; import java.util.concurrent.atomic.*; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.FlowableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.observers.BaseTestConsumer; /** - * A subscriber that records events and allows making assertions about them. + * A {@link Subscriber} implementation that records events and allows making assertions about them. * - *

    You can override the onSubscribe, onNext, onError, onComplete, request and - * cancel methods but not the others (this is by design). - * - *

    The TestSubscriber implements Disposable for convenience where dispose calls cancel. + *

    You can override the {@link #onSubscribe(Subscription)}, {@link #onNext(Object)}, {@link #onError(Throwable)} and + * {@link #onComplete()} methods but not the others (this is by design). * *

    When calling the default request method, you are requesting on behalf of the - * wrapped actual subscriber. + * wrapped actual {@link Subscriber} if any. * * @param the value type */ @@ -49,78 +49,82 @@ public class TestSubscriber private final AtomicLong missedRequested; /** - * Creates a TestSubscriber with Long.MAX_VALUE initial request. + * Creates a {@code TestSubscriber} with {@link Long#MAX_VALUE} initial request amount. * @param the value type - * @return the new TestSubscriber instance. + * @return the new {@code TestSubscriber} instance. + * @see #create(long) */ + @NonNull public static TestSubscriber create() { - return new TestSubscriber(); + return new TestSubscriber<>(); } /** - * Creates a TestSubscriber with the given initial request. + * Creates a {@code TestSubscriber} with the given initial request amount. * @param the value type * @param initialRequested the initial requested amount - * @return the new TestSubscriber instance. + * @return the new {@code TestSubscriber} instance. */ + @NonNull public static TestSubscriber create(long initialRequested) { - return new TestSubscriber(initialRequested); + return new TestSubscriber<>(initialRequested); } /** - * Constructs a forwarding TestSubscriber. + * Constructs a forwarding {@code TestSubscriber}. * @param the value type received - * @param delegate the actual Subscriber to forward events to + * @param delegate the actual {@link Subscriber} to forward events to * @return the new TestObserver instance */ - public static TestSubscriber create(Subscriber delegate) { - return new TestSubscriber(delegate); + public static TestSubscriber create(@NonNull Subscriber delegate) { + return new TestSubscriber<>(delegate); } /** - * Constructs a non-forwarding TestSubscriber with an initial request value of Long.MAX_VALUE. + * Constructs a non-forwarding {@code TestSubscriber} with an initial request value of {@link Long#MAX_VALUE}. */ public TestSubscriber() { this(EmptySubscriber.INSTANCE, Long.MAX_VALUE); } /** - * Constructs a non-forwarding TestSubscriber with the specified initial request value. - *

    The TestSubscriber doesn't validate the initialRequest value so one can + * Constructs a non-forwarding {@code TestSubscriber} with the specified initial request value. + *

    The {@code TestSubscriber} doesn't validate the {@code initialRequest} amount so one can * test sources with invalid values as well. - * @param initialRequest the initial request value + * @param initialRequest the initial request amount */ public TestSubscriber(long initialRequest) { this(EmptySubscriber.INSTANCE, initialRequest); } /** - * Constructs a forwarding TestSubscriber but leaves the requesting to the wrapped subscriber. - * @param downstream the actual Subscriber to forward events to + * Constructs a forwarding {@code TestSubscriber} but leaves the requesting to the wrapped {@link Subscriber}. + * @param downstream the actual {@code Subscriber} to forward events to */ - public TestSubscriber(Subscriber downstream) { + public TestSubscriber(@NonNull Subscriber downstream) { this(downstream, Long.MAX_VALUE); } /** - * Constructs a forwarding TestSubscriber with the specified initial request value. - *

    The TestSubscriber doesn't validate the initialRequest value so one can + * Constructs a forwarding {@code TestSubscriber} with the specified initial request amount + * and an actual {@link Subscriber} to forward events to. + *

    The {@code TestSubscriber} doesn't validate the initialRequest value so one can * test sources with invalid values as well. - * @param actual the actual Subscriber to forward events to - * @param initialRequest the initial request value + * @param actual the actual {@code Subscriber} to forward events to + * @param initialRequest the initial request amount */ - public TestSubscriber(Subscriber actual, long initialRequest) { + public TestSubscriber(@NonNull Subscriber actual, long initialRequest) { super(); if (initialRequest < 0) { throw new IllegalArgumentException("Negative initial request not allowed"); } this.downstream = actual; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.missedRequested = new AtomicLong(initialRequest); } @Override - public void onSubscribe(Subscription s) { + public void onSubscribe(@NonNull Subscription s) { lastThread = Thread.currentThread(); if (s == null) { @@ -153,7 +157,7 @@ protected void onStart() { } @Override - public void onNext(T t) { + public void onNext(@NonNull T t) { if (!checkSubscriptionOnce) { checkSubscriptionOnce = true; if (upstream.get() == null) { @@ -172,19 +176,20 @@ public void onNext(T t) { } @Override - public void onError(Throwable t) { + public void onError(@NonNull Throwable t) { if (!checkSubscriptionOnce) { checkSubscriptionOnce = true; if (upstream.get() == null) { - errors.add(new NullPointerException("onSubscribe not called in proper order")); + errors.add(new IllegalStateException("onSubscribe not called in proper order")); } } try { lastThread = Thread.currentThread(); - errors.add(t); if (t == null) { - errors.add(new IllegalStateException("onError received a null Throwable")); + errors.add(new NullPointerException("onError received a null Throwable")); + } else { + errors.add(t); } downstream.onError(t); @@ -225,8 +230,8 @@ public final void cancel() { } /** - * Returns true if this TestSubscriber has been cancelled. - * @return true if this TestSubscriber has been cancelled + * Returns true if this {@code TestSubscriber} has been cancelled. + * @return true if this {@code TestSubscriber} has been cancelled */ public final boolean isCancelled() { return cancelled; @@ -245,8 +250,8 @@ protected final boolean isDisposed() { // state retrieval methods /** - * Returns true if this TestSubscriber received a subscription. - * @return true if this TestSubscriber received a subscription + * Returns true if this {@code TestSubscriber} received a {@link Subscription} via {@link #onSubscribe(Subscription)}. + * @return true if this {@code TestSubscriber} received a {@link Subscription} via {@link #onSubscribe(Subscription)} */ public final boolean hasSubscription() { return upstream.get() != null; @@ -255,7 +260,7 @@ public final boolean hasSubscription() { // assertion methods /** - * Assert that the onSubscribe method was called exactly once. + * Assert that the {@link #onSubscribe(Subscription)} method was called exactly once. * @return this */ @Override diff --git a/src/main/java/io/reactivex/rxjava3/subscribers/package-info.java b/src/main/java/io/reactivex/rxjava3/subscribers/package-info.java index 1792e0e460..592aea185b 100644 --- a/src/main/java/io/reactivex/rxjava3/subscribers/package-info.java +++ b/src/main/java/io/reactivex/rxjava3/subscribers/package-info.java @@ -1,23 +1,21 @@ -/** +/* * 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. */ /** - * Default wrappers and implementations for Subscriber-based consumer classes and interfaces, - * including disposable and resource-tracking variants and - * the {@link io.reactivex.rxjava3.subscribers.TestSubscriber} that allows unit testing - * {@link io.reactivex.rxjava3.core.Flowable}-based flows. + * Default wrappers and implementations for {@link org.reactivestreams.Subscriber Subscriber}-based consumer classes and interfaces, + * including disposable ({@link io.reactivex.rxjava3.subscribers.DisposableSubscriber DisposableSubscriber}) and resource-tracking + * ({@link io.reactivex.rxjava3.subscribers.ResourceSubscriber ResourceSubscriber}) + * variants and the {@link io.reactivex.rxjava3.subscribers.TestSubscriber TestSubscriber} that allows unit testing + * {@link io.reactivex.rxjava3.core.Flowable Flowable}-based flows. */ package io.reactivex.rxjava3.subscribers; diff --git a/src/main/module/module-info.java b/src/main/module/module-info.java new file mode 100644 index 0000000000..4bed327f1a --- /dev/null +++ b/src/main/module/module-info.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +module io.reactivex.rxjava3 { + exports io.reactivex.rxjava3.annotations; + exports io.reactivex.rxjava3.core; + exports io.reactivex.rxjava3.disposables; + exports io.reactivex.rxjava3.exceptions; + exports io.reactivex.rxjava3.flowables; + exports io.reactivex.rxjava3.functions; + exports io.reactivex.rxjava3.observables; + exports io.reactivex.rxjava3.observers; + exports io.reactivex.rxjava3.operators; + exports io.reactivex.rxjava3.parallel; + exports io.reactivex.rxjava3.plugins; + exports io.reactivex.rxjava3.processors; + exports io.reactivex.rxjava3.schedulers; + exports io.reactivex.rxjava3.subjects; + exports io.reactivex.rxjava3.subscribers; + + requires transitive org.reactivestreams; +} \ No newline at end of file diff --git a/src/test/java/io/reactivex/rxjava3/completable/CapturingUncaughtExceptionHandler.java b/src/test/java/io/reactivex/rxjava3/completable/CapturingUncaughtExceptionHandler.java index 66738a5513..8379925bee 100644 --- a/src/test/java/io/reactivex/rxjava3/completable/CapturingUncaughtExceptionHandler.java +++ b/src/test/java/io/reactivex/rxjava3/completable/CapturingUncaughtExceptionHandler.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.completable; diff --git a/src/test/java/io/reactivex/rxjava3/completable/CompletableRetryTest.java b/src/test/java/io/reactivex/rxjava3/completable/CompletableRetryTest.java index 909ce35669..3d3ad65968 100644 --- a/src/test/java/io/reactivex/rxjava3/completable/CompletableRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/completable/CompletableRetryTest.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-present, RxJava Contributors. +/* + * 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 @@ -20,6 +20,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -113,4 +114,42 @@ public void retryTimesPredicateWithZeroRetries() { assertEquals(1, numberOfSubscribeCalls.get()); } + + @Test + public void untilTrueEmpty() { + Completable.complete() + .retryUntil(() -> true) + .test() + .assertResult(); + } + + @Test + public void untilFalseEmpty() { + Completable.complete() + .retryUntil(() -> false) + .test() + .assertResult(); + } + + @Test + public void untilTrueError() { + Completable.error(new TestException()) + .retryUntil(() -> true) + .test() + .assertFailure(TestException.class); + } + + @Test + public void untilFalseError() { + AtomicInteger counter = new AtomicInteger(); + Completable.defer(() -> { + if (counter.getAndIncrement() == 0) { + return Completable.error(new TestException()); + } + return Completable.complete(); + }) + .retryUntil(() -> false) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/completable/CompletableTest.java b/src/test/java/io/reactivex/rxjava3/completable/CompletableTest.java index ebe1303bd2..9f64b81d12 100644 --- a/src/test/java/io/reactivex/rxjava3/completable/CompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/completable/CompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; @@ -156,11 +155,6 @@ public void complete() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void concatNull() { - Completable.concatArray((Completable[])null); - } - @Test public void concatEmpty() { Completable c = Completable.concatArray(); @@ -214,11 +208,6 @@ public void concatIterableEmpty() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void concatIterableNull() { - Completable.concat((Iterable)null); - } - @Test(expected = NullPointerException.class) public void concatIterableIteratorNull() { Completable c = Completable.concat(new Iterable() { @@ -231,13 +220,6 @@ public Iterator iterator() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void concatIterableWithNull() { - Completable c = Completable.concat(Arrays.asList(normal.completable, (Completable)null)); - - c.blockingAwait(); - } - @Test public void concatIterableSingle() { Completable c = Completable.concat(Collections.singleton(normal.completable)); @@ -349,7 +331,7 @@ public void concatObservableManyOneThrows() { @Test public void concatObservablePrefetch() { - final List requested = new ArrayList(); + final List requested = new ArrayList<>(); Flowable cs = Flowable .just(normal.completable) .repeat(10) @@ -367,11 +349,6 @@ public void accept(long v) { Assert.assertEquals(Arrays.asList(5L, 4L, 4L), requested); } - @Test(expected = NullPointerException.class) - public void createNull() { - Completable.unsafeCreate(null); - } - @Test(expected = NullPointerException.class) public void createOnSubscribeThrowsNPE() { Completable c = Completable.unsafeCreate(new CompletableSource() { @@ -424,11 +401,6 @@ public Completable get() { normal.assertSubscriptions(1); } - @Test(expected = NullPointerException.class) - public void deferNull() { - Completable.defer(null); - } - @Test(expected = NullPointerException.class) public void deferReturnsNull() { Completable c = Completable.defer(new Supplier() { @@ -463,11 +435,6 @@ public Completable get() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void errorSupplierNull() { - Completable.error((Supplier)null); - } - @Test(expected = TestException.class) public void errorSupplierNormal() { Completable c = Completable.error(new Supplier() { @@ -502,11 +469,6 @@ public void errorSupplierThrows() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void errorNull() { - Completable.error((Throwable)null); - } - @Test(expected = TestException.class) public void errorNormal() { Completable c = Completable.error(new TestException()); @@ -514,11 +476,6 @@ public void errorNormal() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void fromCallableNull() { - Completable.fromCallable(null); - } - @Test public void fromCallableNormal() { final AtomicInteger calls = new AtomicInteger(); @@ -545,11 +502,6 @@ public void fromCallableThrows() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void fromFlowableNull() { - Completable.fromPublisher(null); - } - @Test public void fromFlowableEmpty() { Completable c = Completable.fromPublisher(Flowable.empty()); @@ -578,11 +530,6 @@ public Throwable get() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void fromObservableNull() { - Completable.fromObservable(null); - } - @Test public void fromObservableEmpty() { Completable c = Completable.fromObservable(Observable.empty()); @@ -611,11 +558,6 @@ public Throwable get() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void fromActionNull() { - Completable.fromAction(null); - } - @Test public void fromActionNormal() { final AtomicInteger calls = new AtomicInteger(); @@ -642,11 +584,6 @@ public void fromActionThrows() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void fromSingleNull() { - Completable.fromSingle(null); - } - @Test public void fromSingleNormal() { Completable c = Completable.fromSingle(Single.just(1)); @@ -666,11 +603,6 @@ public Throwable get() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void mergeNull() { - Completable.mergeArray((Completable[])null); - } - @Test public void mergeEmpty() { Completable c = Completable.mergeArray(); @@ -724,11 +656,6 @@ public void mergeIterableEmpty() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void mergeIterableNull() { - Completable.merge((Iterable)null); - } - @Test(expected = NullPointerException.class) public void mergeIterableIteratorNull() { Completable c = Completable.merge(new Iterable() { @@ -741,13 +668,6 @@ public Iterator iterator() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void mergeIterableWithNull() { - Completable c = Completable.merge(Arrays.asList(normal.completable, (Completable)null)); - - c.blockingAwait(); - } - @Test public void mergeIterableSingle() { Completable c = Completable.merge(Collections.singleton(normal.completable)); @@ -859,7 +779,7 @@ public void mergeObservableManyOneThrows() { @Test public void mergeObservableMaxConcurrent() { - final List requested = new ArrayList(); + final List requested = new ArrayList<>(); Flowable cs = Flowable .just(normal.completable) .repeat(10) @@ -878,11 +798,6 @@ public void accept(long v) { Assert.assertEquals(Arrays.asList(5L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), requested); } - @Test(expected = NullPointerException.class) - public void mergeDelayErrorNull() { - Completable.mergeArrayDelayError((Completable[])null); - } - @Test public void mergeDelayErrorEmpty() { Completable c = Completable.mergeArrayDelayError(); @@ -940,11 +855,6 @@ public void mergeDelayErrorIterableEmpty() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void mergeDelayErrorIterableNull() { - Completable.mergeDelayError((Iterable)null); - } - @Test(expected = NullPointerException.class) public void mergeDelayErrorIterableIteratorNull() { Completable c = Completable.mergeDelayError(new Iterable() { @@ -957,13 +867,6 @@ public Iterator iterator() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void mergeDelayErrorIterableWithNull() { - Completable c = Completable.mergeDelayError(Arrays.asList(normal.completable, (Completable)null)); - - c.blockingAwait(); - } - @Test public void mergeDelayErrorIterableSingle() { Completable c = Completable.mergeDelayError(Collections.singleton(normal.completable)); @@ -1079,7 +982,7 @@ public void mergeDelayErrorObservableManyOneThrows() { @Test public void mergeDelayErrorObservableMaxConcurrent() { - final List requested = new ArrayList(); + final List requested = new ArrayList<>(); Flowable cs = Flowable .just(normal.completable) .repeat(10) @@ -1204,16 +1107,6 @@ public void onComplete() { Assert.assertEquals(0, calls.get()); } - @Test(expected = NullPointerException.class) - public void timerUnitNull() { - Completable.timer(1, null); - } - - @Test(expected = NullPointerException.class) - public void timerSchedulerNull() { - Completable.timer(1, TimeUnit.SECONDS, null); - } - @Test public void usingNormalEager() { final AtomicInteger dispose = new AtomicInteger(); @@ -1236,7 +1129,7 @@ public void accept(Integer d) { }); final AtomicBoolean disposedFirst = new AtomicBoolean(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); c.subscribe(new CompletableObserver() { @Override @@ -1282,7 +1175,7 @@ public void accept(Integer d) { }, false); final AtomicBoolean disposedFirst = new AtomicBoolean(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); c.subscribe(new CompletableObserver() { @Override @@ -1398,32 +1291,6 @@ public void onComplete() { Assert.assertFalse(complete.get()); } - @Test(expected = NullPointerException.class) - public void usingResourceSupplierNull() { - Completable.using(null, new Function() { - @Override - public Completable apply(Object v) { - return normal.completable; - } - }, new Consumer() { - @Override - public void accept(Object v) { } - }); - } - - @Test(expected = NullPointerException.class) - public void usingMapperNull() { - Completable.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null, new Consumer() { - @Override - public void accept(Object v) { } - }); - } - @Test(expected = NullPointerException.class) public void usingMapperReturnsNull() { Completable c = Completable.using(new Supplier() { @@ -1444,21 +1311,6 @@ public void accept(Object v) { } c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void usingDisposeNull() { - Completable.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new Function() { - @Override - public Completable apply(Object v) { - return normal.completable; - } - }, null); - } - @Test(expected = TestException.class) public void usingResourceThrows() { Completable c = Completable.using(new Supplier() { @@ -1530,11 +1382,6 @@ public Completable apply(Completable n) { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void composeNull() { - error.completable.compose(null); - } - @Test public void concatWithNormal() { Completable c = normal.completable.concatWith(normal.completable); @@ -1551,27 +1398,12 @@ public void concatWithError() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void concatWithNull() { - normal.completable.concatWith(null); - } - - @Test(expected = NullPointerException.class) - public void delayUnitNull() { - normal.completable.delay(1, null); - } - - @Test(expected = NullPointerException.class) - public void delaySchedulerNull() { - normal.completable.delay(1, TimeUnit.SECONDS, null); - } - @Test public void delayNormal() throws InterruptedException { Completable c = normal.completable.delay(250, TimeUnit.MILLISECONDS); final AtomicBoolean done = new AtomicBoolean(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); c.subscribe(new CompletableObserver() { @Override @@ -1611,7 +1443,7 @@ public void delayErrorImmediately() throws InterruptedException { final Completable c = error.completable.delay(250, TimeUnit.MILLISECONDS, scheduler); final AtomicBoolean done = new AtomicBoolean(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); c.subscribe(new CompletableObserver() { @Override @@ -1645,7 +1477,7 @@ public void delayErrorToo() throws InterruptedException { Completable c = error.completable.delay(250, TimeUnit.MILLISECONDS, Schedulers.computation(), true); final AtomicBoolean done = new AtomicBoolean(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); c.subscribe(new CompletableObserver() { @Override @@ -1712,11 +1544,6 @@ public void run() { Assert.assertEquals(0, calls.get()); } - @Test(expected = NullPointerException.class) - public void doOnCompleteNull() { - normal.completable.doOnComplete(null); - } - @Test(expected = TestException.class) public void doOnCompleteThrows() { Completable c = normal.completable.doOnComplete(new Action() { @@ -1794,11 +1621,6 @@ public void onComplete() { Assert.assertEquals(1, calls.get()); } - @Test(expected = NullPointerException.class) - public void doOnDisposeNull() { - normal.completable.doOnDispose(null); - } - @Test public void doOnDisposeThrows() { List errors = TestHelper.trackPluginErrors(); @@ -1833,7 +1655,7 @@ public void onComplete() { @Test public void doOnErrorNoError() { - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); Completable c = normal.completable.doOnError(new Consumer() { @Override @@ -1849,7 +1671,7 @@ public void accept(Throwable e) { @Test public void doOnErrorHasError() { - final AtomicReference err = new AtomicReference(); + final AtomicReference err = new AtomicReference<>(); Completable c = error.completable.doOnError(new Consumer() { @Override @@ -1868,11 +1690,6 @@ public void accept(Throwable e) { Assert.assertTrue(err.get() instanceof TestException); } - @Test(expected = NullPointerException.class) - public void doOnErrorNull() { - normal.completable.doOnError(null); - } - @Test public void doOnErrorThrows() { Completable c = error.completable.doOnError(new Consumer() { @@ -1908,11 +1725,6 @@ public void accept(Disposable d) { Assert.assertEquals(10, calls.get()); } - @Test(expected = NullPointerException.class) - public void doOnSubscribeNull() { - normal.completable.doOnSubscribe(null); - } - @Test(expected = TestException.class) public void doOnSubscribeThrows() { Completable c = normal.completable.doOnSubscribe(new Consumer() { @@ -1960,11 +1772,6 @@ public void run() { Assert.assertEquals(1, calls.get()); } - @Test(expected = NullPointerException.class) - public void liftNull() { - normal.completable.lift(null); - } - @Test(expected = NullPointerException.class) public void liftReturnsNull() { Completable c = normal.completable.lift(new CompletableOperator() { @@ -2015,11 +1822,6 @@ public void liftOnErrorComplete() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void mergeWithNull() { - normal.completable.mergeWith(null); - } - @Test public void mergeWithNormal() { Completable c = normal.completable.mergeWith(normal.completable); @@ -2029,15 +1831,10 @@ public void mergeWithNormal() { normal.assertSubscriptions(2); } - @Test(expected = NullPointerException.class) - public void observeOnNull() { - normal.completable.observeOn(null); - } - @Test public void observeOnNormal() throws InterruptedException { - final AtomicReference name = new AtomicReference(); - final AtomicReference err = new AtomicReference(); + final AtomicReference name = new AtomicReference<>(); + final AtomicReference err = new AtomicReference<>(); final CountDownLatch cdl = new CountDownLatch(1); Completable c = normal.completable.observeOn(Schedulers.computation()); @@ -2069,8 +1866,8 @@ public void onError(Throwable e) { @Test public void observeOnError() throws InterruptedException { - final AtomicReference name = new AtomicReference(); - final AtomicReference err = new AtomicReference(); + final AtomicReference name = new AtomicReference<>(); + final AtomicReference err = new AtomicReference<>(); final CountDownLatch cdl = new CountDownLatch(1); Completable c = error.completable.observeOn(Schedulers.computation()); @@ -2120,16 +1917,6 @@ public boolean test(Throwable e) { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void onErrorCompleteNull() { - error.completable.onErrorComplete(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorResumeNextNull() { - error.completable.onErrorResumeNext(null); - } - @Test public void onErrorResumeNextFunctionReturnsNull() { Completable c = error.completable.onErrorResumeNext(new Function() { @@ -2195,7 +1982,7 @@ public Completable apply(Throwable v) { @Test public void repeatNormal() { - final AtomicReference err = new AtomicReference(); + final AtomicReference err = new AtomicReference<>(); final AtomicInteger calls = new AtomicInteger(); Completable c = Completable.fromCallable(new Callable() { @@ -2314,16 +2101,6 @@ public boolean getAsBoolean() { Assert.assertEquals(5, calls.get()); } - @Test(expected = NullPointerException.class) - public void repeatUntilNull() { - normal.completable.repeatUntil(null); - } - - @Test(expected = NullPointerException.class) - public void repeatWhenNull() { - normal.completable.repeatWhen(null); - } - @Test public void retryNormal() { Completable c = normal.completable.retry(); @@ -2402,11 +2179,6 @@ public boolean test(Throwable e) { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void retryPredicateNull() { - error.completable.retry((Predicate)null); - } - @Test public void retryPredicate5Times() { final AtomicInteger calls = new AtomicInteger(5); @@ -2500,7 +2272,7 @@ public void run() { @Test public void subscribeTwoCallbacksNormal() { - final AtomicReference err = new AtomicReference(); + final AtomicReference err = new AtomicReference<>(); final AtomicBoolean complete = new AtomicBoolean(); normal.completable.subscribe(new Action() { @Override @@ -2520,7 +2292,7 @@ public void accept(Throwable e) { @Test public void subscribeTwoCallbacksError() { - final AtomicReference err = new AtomicReference(); + final AtomicReference err = new AtomicReference<>(); final AtomicBoolean complete = new AtomicBoolean(); error.completable.subscribe(new Action() { @Override @@ -2538,27 +2310,11 @@ public void accept(Throwable e) { Assert.assertFalse("Not completed", complete.get()); } - @Test(expected = NullPointerException.class) - public void subscribeTwoCallbacksFirstNull() { - normal.completable.subscribe(new Action() { - @Override - public void run() { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void subscribeTwoCallbacksSecondNull() { - normal.completable.subscribe(new Action() { - @Override - public void run() { } - }, null); - } - @Test public void subscribeTwoCallbacksCompleteThrows() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference err = new AtomicReference(); + final AtomicReference err = new AtomicReference<>(); normal.completable.subscribe(new Action() { @Override public void run() { throw new TestException(); } @@ -2596,7 +2352,7 @@ public void run() { } @Test public void subscribeObserverNormal() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); normal.completable.toObservable().subscribe(to); @@ -2607,7 +2363,7 @@ public void subscribeObserverNormal() { @Test public void subscribeObserverError() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); error.completable.toObservable().subscribe(to); @@ -2651,29 +2407,9 @@ public void run() { } } - @Test(expected = NullPointerException.class) - public void subscribeActionNull() { - normal.completable.subscribe((Action)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeSubscriberNull() { - normal.completable.toFlowable().subscribe((Subscriber)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeObserverNull() { - normal.completable.toObservable().subscribe((Observer)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeCompletableSubscriberNull() { - normal.completable.subscribe((CompletableObserver)null); - } - @Test public void subscribeSubscriberNormal() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); normal.completable.toFlowable().subscribe(ts); @@ -2684,7 +2420,7 @@ public void subscribeSubscriberNormal() { @Test public void subscribeSubscriberError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); error.completable.toFlowable().subscribe(ts); @@ -2693,14 +2429,9 @@ public void subscribeSubscriberError() { ts.assertError(TestException.class); } - @Test(expected = NullPointerException.class) - public void subscribeOnNull() { - normal.completable.subscribeOn(null); - } - @Test public void subscribeOnNormal() { - final AtomicReference name = new AtomicReference(); + final AtomicReference name = new AtomicReference<>(); Completable c = Completable.unsafeCreate(new CompletableSource() { @Override @@ -2717,7 +2448,7 @@ public void subscribe(CompletableObserver observer) { @Test public void subscribeOnError() { - final AtomicReference name = new AtomicReference(); + final AtomicReference name = new AtomicReference<>(); Completable c = Completable.unsafeCreate(new CompletableSource() { @Override @@ -2763,21 +2494,6 @@ public Object call() throws Exception { normal.assertSubscriptions(0); } - @Test(expected = NullPointerException.class) - public void timeoutUnitNull() { - normal.completable.timeout(1, null); - } - - @Test(expected = NullPointerException.class) - public void timeoutSchedulerNull() { - normal.completable.timeout(1, TimeUnit.SECONDS, (Scheduler)null); - } - - @Test(expected = NullPointerException.class) - public void timeoutOtherNull() { - normal.completable.timeout(1, TimeUnit.SECONDS, (Completable)null); - } - @Test public void toNormal() { normal.completable @@ -2818,11 +2534,6 @@ public Flowable apply(Completable v) { .assertComplete(); } - @Test(expected = NullPointerException.class) - public void toNull() { - normal.completable.to(null); - } - @Test public void toFlowableNormal() { normal.completable.toFlowable().blockingForEach(Functions.emptyConsumer()); @@ -2863,11 +2574,6 @@ public Object get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toSingleSupplierNull() { - normal.completable.toSingle(null); - } - @Test(expected = NullPointerException.class) public void toSingleSupplierReturnsNull() { normal.completable.toSingle(new Supplier() { @@ -2896,14 +2602,9 @@ public void toSingleDefaultNormal() { Assert.assertEquals((Integer)1, normal.completable.toSingleDefault(1).blockingGet()); } - @Test(expected = NullPointerException.class) - public void toSingleDefaultNull() { - normal.completable.toSingleDefault(null); - } - @Test public void unsubscribeOnNormal() throws InterruptedException { - final AtomicReference name = new AtomicReference(); + final AtomicReference name = new AtomicReference<>(); final CountDownLatch cdl = new CountDownLatch(1); normal.completable.delay(1, TimeUnit.SECONDS) @@ -2942,11 +2643,6 @@ public void onComplete() { Assert.assertTrue(name.get().startsWith("RxComputation")); } - @Test(expected = NullPointerException.class) - public void ambArrayNull() { - Completable.ambArray((Completable[])null); - } - @Test public void ambArrayEmpty() { Completable c = Completable.ambArray(); @@ -3010,7 +2706,7 @@ public void ambArrayOneFiresError() { Completable c = Completable.ambArray(c1, c2); - final AtomicReference complete = new AtomicReference(); + final AtomicReference complete = new AtomicReference<>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer() { @Override @@ -3072,7 +2768,7 @@ public void ambArraySecondFiresError() { Completable c = Completable.ambArray(c1, c2); - final AtomicReference complete = new AtomicReference(); + final AtomicReference complete = new AtomicReference<>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer() { @Override @@ -3106,11 +2802,6 @@ public void ambIterableEmpty() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void ambIterableNull() { - Completable.amb((Iterable)null); - } - @Test public void ambIterableIteratorNull() { Completable.amb(new Iterable() { @@ -3184,11 +2875,6 @@ public void ambIterableIteratorNextThrows() { .assertError(TestException.class); } - @Test(expected = NullPointerException.class) - public void ambWithNull() { - normal.completable.ambWith(null); - } - @Test public void ambWithArrayOneFires() { PublishProcessor pp1 = PublishProcessor.create(); @@ -3231,7 +2917,7 @@ public void ambWithArrayOneFiresError() { Completable c = c1.ambWith(c2); - final AtomicReference complete = new AtomicReference(); + final AtomicReference complete = new AtomicReference<>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer() { @Override @@ -3293,7 +2979,7 @@ public void ambWithArraySecondFiresError() { Completable c = c1.ambWith(c2); - final AtomicReference complete = new AtomicReference(); + final AtomicReference complete = new AtomicReference<>(); c.subscribe(Functions.EMPTY_ACTION, new Consumer() { @Override @@ -3356,7 +3042,7 @@ public Object call() throws Exception { } })); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); c.subscribe(ts); @@ -3373,7 +3059,7 @@ public void startWithFlowableError() { Flowable c = normal.completable .startWith(Flowable.error(new TestException())); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); c.subscribe(ts); @@ -3396,7 +3082,7 @@ public Object call() throws Exception { } })); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o.subscribe(to); @@ -3413,7 +3099,7 @@ public void startWithObservableError() { Observable o = normal.completable .startWith(Observable.error(new TestException())); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o.subscribe(to); @@ -3424,31 +3110,16 @@ public void startWithObservableError() { to.assertNotComplete(); } - @Test(expected = NullPointerException.class) - public void startWithCompletableNull() { - normal.completable.startWith((Completable)null); - } - - @Test(expected = NullPointerException.class) - public void startWithFlowableNull() { - normal.completable.startWith((Flowable)null); - } - - @Test(expected = NullPointerException.class) - public void startWithObservableNull() { - normal.completable.startWith((Observable)null); - } - @Test public void andThen() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Completable.complete().andThen(Flowable.just("foo")).subscribe(ts); ts.request(1); ts.assertValue("foo"); ts.assertComplete(); ts.assertNoErrors(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Completable.complete().andThen(Observable.just("foo")).subscribe(to); to.assertValue("foo"); to.assertComplete(); @@ -3543,7 +3214,7 @@ public void accept(Integer t) { } }; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Completable.using(new Supplier() { @Override @@ -3613,7 +3284,7 @@ public void subscribeActionReportsUnsubscribedAfter() { PublishSubject stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); - final AtomicReference disposableRef = new AtomicReference(); + final AtomicReference disposableRef = new AtomicReference<>(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { @@ -3695,7 +3366,7 @@ public void accept(Throwable e) { } @Test public void andThenSubscribeOn() { - TestSubscriberEx ts = new TestSubscriberEx(0); + TestSubscriberEx ts = new TestSubscriberEx<>(0); TestScheduler scheduler = new TestScheduler(); Completable.complete().andThen(Flowable.just("foo").delay(1, TimeUnit.SECONDS, scheduler)).subscribe(ts); @@ -3712,7 +3383,7 @@ public void andThenSubscribeOn() { @Test public void andThenSingleNever() { - TestSubscriberEx ts = new TestSubscriberEx(0); + TestSubscriberEx ts = new TestSubscriberEx<>(0); Completable.never().andThen(Single.just("foo")).toFlowable().subscribe(ts); ts.request(1); ts.assertNoValues(); @@ -3721,7 +3392,7 @@ public void andThenSingleNever() { @Test public void andThenSingleError() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); final AtomicBoolean hasRun = new AtomicBoolean(false); final Exception e = new Exception(); Completable.error(e) @@ -3740,7 +3411,7 @@ public void subscribeActual(SingleObserver observer) { @Test public void andThenSingleSubscribeOn() { - TestSubscriberEx ts = new TestSubscriberEx(0); + TestSubscriberEx ts = new TestSubscriberEx<>(0); TestScheduler scheduler = new TestScheduler(); Completable.complete().andThen(Single.just("foo").delay(1, TimeUnit.SECONDS, scheduler)).toFlowable().subscribe(ts); @@ -3830,11 +3501,6 @@ public void run() { Assert.assertEquals(0, calls.get()); } - @Test(expected = NullPointerException.class) - public void doOnCompletedNull() { - normal.completable.doOnComplete(null); - } - @Test(expected = TestException.class) public void doOnCompletedThrows() { Completable c = normal.completable.doOnComplete(new Action() { @@ -3901,11 +3567,6 @@ public void run() { Assert.assertTrue("Closure not called", doneAfter.get()); } - @Test(expected = NullPointerException.class) - public void doAfterTerminateNull() { - normal.completable.doAfterTerminate(null); - } - @Test public void subscribeEmptyOnError() { expectUncaughtTestException(new Action() { @@ -3939,16 +3600,6 @@ public void run() { }); } - @Test(expected = NullPointerException.class) - public void andThenCompletableNull() { - normal.completable.andThen((Completable)null); - } - - @Test(expected = NullPointerException.class) - public void andThenFlowableNull() { - normal.completable.andThen((Observable)null); - } - @Test public void andThenCompletableNormal() { final AtomicBoolean run = new AtomicBoolean(); @@ -3992,7 +3643,7 @@ public Object call() throws Exception { } })); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); c.subscribe(ts); @@ -4009,7 +3660,7 @@ public void andThenFlowableError() { Flowable c = normal.completable .andThen(Flowable.error(new TestException())); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); c.subscribe(ts); @@ -4025,7 +3676,7 @@ public void usingFactoryThrows() throws Throwable { @SuppressWarnings("unchecked") Consumer onDispose = mock(Consumer.class); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Completable.using(new Supplier() { @Override @@ -4056,7 +3707,7 @@ public void accept(Integer t) { } }; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Completable.using(new Supplier() { @Override @@ -4126,7 +3777,7 @@ public void subscribeReportsUnsubscribed() { @Test public void hookSubscribeStart() throws Throwable { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Completable completable = Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver observer) { @@ -4156,7 +3807,7 @@ public void onStart() { @Test public void onErrorCompleteFunctionThrows() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); error.completable.onErrorComplete(new Predicate() { @Override @@ -4185,7 +3836,7 @@ public void subscribeAction2ReportsUnsubscribedAfter() { PublishSubject stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); - final AtomicReference disposableRef = new AtomicReference(); + final AtomicReference disposableRef = new AtomicReference<>(); Disposable completableSubscription = completable.subscribe(new Action() { @Override public void run() { @@ -4207,7 +3858,7 @@ public void subscribeAction2ReportsUnsubscribedOnErrorAfter() { PublishSubject stringSubject = PublishSubject.create(); Completable completable = stringSubject.ignoreElements(); - final AtomicReference disposableRef = new AtomicReference(); + final AtomicReference disposableRef = new AtomicReference<>(); Disposable completableSubscription = completable.subscribe(Functions.EMPTY_ACTION, new Consumer() { @Override @@ -4242,7 +3893,7 @@ public void accept(Integer integer) { @Test public void andThenNever() { - TestSubscriberEx ts = new TestSubscriberEx(0); + TestSubscriberEx ts = new TestSubscriberEx<>(0); Completable.never().andThen(Flowable.just("foo")).subscribe(ts); ts.request(1); ts.assertNoValues(); @@ -4251,13 +3902,13 @@ public void andThenNever() { @Test public void andThenError() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); final AtomicBoolean hasRun = new AtomicBoolean(false); final Exception e = new Exception(); Completable.unsafeCreate(new CompletableSource() { @Override public void subscribe(CompletableObserver co) { - co.onSubscribe(Disposables.empty()); + co.onSubscribe(Disposable.empty()); co.onError(e); } }) @@ -4278,7 +3929,7 @@ public void subscribe(Subscriber s) { @Test public void andThenSingle() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Completable.complete().andThen(Single.just("foo")).toFlowable().subscribe(ts); ts.request(1); ts.assertValue("foo"); @@ -4286,11 +3937,6 @@ public void andThenSingle() { ts.assertNoErrors(); } - @Test(expected = NullPointerException.class) - public void fromFutureNull() { - Completable.fromFuture(null); - } - @Test public void fromFutureNormal() { ExecutorService exec = Executors.newSingleThreadExecutor(); @@ -4333,11 +3979,6 @@ public void run() { } } - @Test(expected = NullPointerException.class) - public void fromRunnableNull() { - Completable.fromRunnable(null); - } - @Test public void fromRunnableNormal() { final AtomicInteger calls = new AtomicInteger(); @@ -4364,41 +4005,6 @@ public void fromRunnableThrows() { c.blockingAwait(); } - @Test(expected = NullPointerException.class) - public void doOnErrorNullValue() { - Completable.complete().doOnError(null); - } - - @Test(expected = NullPointerException.class) - public void doOnSubscribeNullValue() { - Completable.complete().doOnSubscribe(null); - } - - @Test(expected = NullPointerException.class) - public void doAfterTerminateNullValue() { - Completable.complete().doAfterTerminate(null); - } - - @Test(expected = NullPointerException.class) - public void doOnTerminateNullValue() { - Completable.complete().doOnTerminate(null); - } - - @Test(expected = NullPointerException.class) - public void doOnCompleteNullValue() { - Completable.complete().doOnComplete(null); - } - - @Test(expected = NullPointerException.class) - public void doOnDisposeNullValue() { - Completable.complete().doOnDispose(null); - } - - @Test(expected = NullPointerException.class) - public void doOnEventNullValue() { - Completable.complete().doOnEvent(null); - } - @Test public void doOnEventComplete() { final AtomicInteger atomicInteger = new AtomicInteger(0); diff --git a/src/test/java/io/reactivex/rxjava3/completable/CompletableTimerTest.java b/src/test/java/io/reactivex/rxjava3/completable/CompletableTimerTest.java index 17f1e7beae..cad1780eb6 100644 --- a/src/test/java/io/reactivex/rxjava3/completable/CompletableTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/completable/CompletableTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/core/BackpressureEnumTest.java b/src/test/java/io/reactivex/rxjava3/core/BackpressureEnumTest.java index 583899167d..e007bfe8ef 100644 --- a/src/test/java/io/reactivex/rxjava3/core/BackpressureEnumTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/BackpressureEnumTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.core; import static org.junit.Assert.*; diff --git a/src/test/java/io/reactivex/rxjava3/core/ConverterTest.java b/src/test/java/io/reactivex/rxjava3/core/ConverterTest.java index 7a39c85cfd..1d0ddd5e40 100644 --- a/src/test/java/io/reactivex/rxjava3/core/ConverterTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/ConverterTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -175,7 +175,17 @@ public void compositeTest() { .assertValue(1); } + /** + * Two argument type. + * @param the input type + * @param the output type + */ interface A { } + + /** + * One argument type. + * @param the type + */ interface B { } private static ObservableConverter, B> testObservableConverterCreator() { diff --git a/src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java b/src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java new file mode 100644 index 0000000000..d429b588f7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/core/DisposeTaskTest.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.core; + +import static org.junit.Assert.fail; +import static org.testng.Assert.assertTrue; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Scheduler.DisposeTask; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class DisposeTaskTest extends RxJavaTest { + + @Test + public void runnableThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + + Scheduler.Worker worker = Schedulers.single().createWorker(); + + DisposeTask task = new DisposeTask(() -> { + throw new TestException(); + }, worker); + + try { + task.run(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + assertTrue(worker.isDisposed()); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java b/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java index 1c1a6adf59..b26262c15c 100644 --- a/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/NotificationTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,10 +42,21 @@ public void valueOfOnCompleteIsNull() { public void notEqualsToObject() { Notification n1 = Notification.createOnNext(0); assertNotEquals(0, n1); + assertNotEquals(n1, 0); Notification n2 = Notification.createOnError(new TestException()); assertNotEquals(0, n2); + assertNotEquals(n2, 0); Notification n3 = Notification.createOnComplete(); assertNotEquals(0, n3); + assertNotEquals(n3, 0); + } + + @Test + public void twoEqual() { + Notification n1 = Notification.createOnNext(0); + Notification n2 = Notification.createOnNext(0); + assertEquals(n1, n2); + assertEquals(n2, n1); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java b/src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java new file mode 100644 index 0000000000..077171acdb --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/core/PeriodicDirectTaskTest.java @@ -0,0 +1,57 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.core; + +import static org.junit.Assert.fail; +import static org.testng.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Scheduler.PeriodicDirectTask; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class PeriodicDirectTaskTest extends RxJavaTest { + + @Test + public void runnableThrows() { + List errors = TestHelper.trackPluginErrors(); + try { + Scheduler.Worker worker = Schedulers.single().createWorker(); + + PeriodicDirectTask task = new PeriodicDirectTask(() -> { + throw new TestException(); + }, worker); + + try { + task.run(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + assertTrue(worker.isDisposed()); + + task.run(); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/core/Retry.java b/src/test/java/io/reactivex/rxjava3/core/Retry.java index a738e0e3c7..5d543afb1c 100644 --- a/src/test/java/io/reactivex/rxjava3/core/Retry.java +++ b/src/test/java/io/reactivex/rxjava3/core/Retry.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -75,4 +75,4 @@ public Statement apply(Statement base, Description description) { private Statement statement(final Statement base, final Description description) { return new RetryStatement(base, description); } -} \ No newline at end of file +} diff --git a/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java b/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java index d903a87f0c..1e6d9fd33d 100644 --- a/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/RxJavaTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.core; import java.util.concurrent.TimeUnit; @@ -20,9 +18,13 @@ import org.junit.*; import org.junit.rules.Timeout; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverableRule; + public abstract class RxJavaTest { @Rule public Timeout globalTimeout = new Timeout(5, TimeUnit.MINUTES); + @Rule + public final SuppressUndeliverableRule suppressUndeliverableRule = new SuppressUndeliverableRule(); /** * Announce creates a log print preventing Travis CI from killing the build. diff --git a/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java b/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java new file mode 100644 index 0000000000..bbb36f1594 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/core/SchedulerTest.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class SchedulerTest { + private static final String DRIFT_USE_NANOTIME = "rx3.scheduler.use-nanotime"; + + @After + public void cleanup() { + // reset value to default in order to not influence other tests + Scheduler.IS_DRIFT_USE_NANOTIME = false; + } + + @Test + public void driftUseNanoTimeNotSetByDefault() { + assertFalse(Scheduler.IS_DRIFT_USE_NANOTIME); + assertFalse(Boolean.getBoolean(DRIFT_USE_NANOTIME)); + } + + @Test + public void computeNow_currentTimeMillis() { + TimeUnit unit = TimeUnit.MILLISECONDS; + assertTrue(isInRange(System.currentTimeMillis(), Scheduler.computeNow(unit), unit, 250, TimeUnit.MILLISECONDS)); + } + + @Test + public void computeNow_nanoTime() { + TimeUnit unit = TimeUnit.NANOSECONDS; + Scheduler.IS_DRIFT_USE_NANOTIME = true; + + assertFalse(isInRange(System.currentTimeMillis(), Scheduler.computeNow(unit), unit, 250, TimeUnit.MILLISECONDS)); + assertTrue(isInRange(System.nanoTime(), Scheduler.computeNow(unit), TimeUnit.NANOSECONDS, 250, TimeUnit.MILLISECONDS)); + } + + private boolean isInRange(long start, long stop, TimeUnit source, long maxDiff, TimeUnit diffUnit) { + long diff = Math.abs(stop - start); + return diffUnit.convert(diff, source) <= maxDiff; + } + + @Test + public void clockDriftCalculation() { + assertEquals(100_000_000L, Scheduler.computeClockDrift(100, "milliseconds")); + + assertEquals(2_000_000_000L, Scheduler.computeClockDrift(2, "seconds")); + + assertEquals(180_000_000_000L, Scheduler.computeClockDrift(3, "minutes")); + + assertEquals(240_000_000_000L, Scheduler.computeClockDrift(4, "random")); + + assertEquals(300_000_000_000L, Scheduler.computeClockDrift(5, null)); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/core/TransformerTest.java b/src/test/java/io/reactivex/rxjava3/core/TransformerTest.java index 8f9f102a04..c672196b60 100644 --- a/src/test/java/io/reactivex/rxjava3/core/TransformerTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/TransformerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,6 +18,7 @@ import org.junit.Test; import org.reactivestreams.Publisher; +import io.reactivex.rxjava3.core.ConverterTest.*; import io.reactivex.rxjava3.exceptions.TestException; public class TransformerTest extends RxJavaTest { @@ -127,9 +128,6 @@ public void flowableGenericsSignatureTest() { Flowable.just(a).compose(TransformerTest.testFlowableTransformerCreator()); } - interface A { } - interface B { } - private static ObservableTransformer, B> testObservableTransformerCreator() { return new ObservableTransformer, B>() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/core/XFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/core/XFlatMapTest.java index 7efeee401d..16ebfcb9a3 100644 --- a/src/test/java/io/reactivex/rxjava3/core/XFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/core/XFlatMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ import org.reactivestreams.Publisher; import io.reactivex.rxjava3.exceptions.TestException; -import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -224,7 +224,7 @@ public Completable apply(Integer v) throws Exception { } @Test - public void observableFlowable() throws Exception { + public void observableObservable() throws Exception { List errors = TestHelper.trackPluginErrors(); try { TestObserver to = Observable.just(1) @@ -504,8 +504,213 @@ public Completable apply(Integer v) throws Exception { } } + @Test + public void singlePublisher() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestSubscriber ts = Single.just(1) + .subscribeOn(Schedulers.io()) + .flatMapPublisher(new Function>() { + @Override + public Publisher apply(Integer v) throws Exception { + sleep(); + return Flowable.error(new TestException()); + } + }) + .test(); + + cb.await(); + + beforeCancelSleep(ts); + + ts.cancel(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + ts.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void singleCombiner() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Single.just(1) + .subscribeOn(Schedulers.io()) + .flatMap(new Function>() { + @Override + public Single apply(Integer v) throws Exception { + sleep(); + return Single.error(new TestException()); + } + }, (a, b) -> a + b) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void singleObservable() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Single.just(1) + .subscribeOn(Schedulers.io()) + .flatMapObservable(new Function>() { + @Override + public Observable apply(Integer v) throws Exception { + sleep(); + return Observable.error(new TestException()); + } + }) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void singleNotificationSuccess() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Single.just(1) + .subscribeOn(Schedulers.io()) + .flatMap( + new Function>() { + @Override + public Single apply(Integer v) throws Exception { + sleep(); + return Single.error(new TestException()); + } + }, + new Function>() { + @Override + public Single apply(Throwable v) throws Exception { + sleep(); + return Single.error(new TestException()); + } + } + ) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void singleNotificationError() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Single.error(new TestException()) + .subscribeOn(Schedulers.io()) + .flatMap( + new Function>() { + @Override + public Single apply(Integer v) throws Exception { + sleep(); + return Single.error(new TestException()); + } + }, + new Function>() { + @Override + public Single apply(Throwable v) throws Exception { + sleep(); + return Single.error(new TestException()); + } + } + ) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + @Test public void maybeSingle() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Maybe.just(1) + .subscribeOn(Schedulers.io()) + .flatMapSingle(new Function>() { + @Override + public Single apply(Integer v) throws Exception { + sleep(); + return Single.error(new TestException()); + } + }) + .toSingle() + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void maybeSingle2() throws Exception { List errors = TestHelper.trackPluginErrors(); try { TestObserver to = Maybe.just(1) @@ -566,6 +771,240 @@ public Maybe apply(Integer v) throws Exception { } } + @Test + public void maybePublisher() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestSubscriber ts = Maybe.just(1) + .subscribeOn(Schedulers.io()) + .flatMapPublisher(new Function>() { + @Override + public Publisher apply(Integer v) throws Exception { + sleep(); + return Flowable.error(new TestException()); + } + }) + .test(); + + cb.await(); + + beforeCancelSleep(ts); + + ts.cancel(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + ts.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void maybeObservable() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Maybe.just(1) + .subscribeOn(Schedulers.io()) + .flatMapObservable(new Function>() { + @Override + public Observable apply(Integer v) throws Exception { + sleep(); + return Observable.error(new TestException()); + } + }) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void maybeNotificationSuccess() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Maybe.just(1) + .subscribeOn(Schedulers.io()) + .flatMap( + new Function>() { + @Override + public Maybe apply(Integer v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, + new Function>() { + @Override + public Maybe apply(Throwable v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, + new Supplier>() { + @Override + public Maybe get() throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + } + ) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void maybeNotificationError() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Maybe.error(new TestException()) + .subscribeOn(Schedulers.io()) + .flatMap( + new Function>() { + @Override + public Maybe apply(Integer v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, + new Function>() { + @Override + public Maybe apply(Throwable v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, + new Supplier>() { + @Override + public Maybe get() throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + } + ) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void maybeNotificationEmpty() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Maybe.empty() + .subscribeOn(Schedulers.io()) + .flatMap( + new Function>() { + @Override + public Maybe apply(Integer v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, + new Function>() { + @Override + public Maybe apply(Throwable v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, + new Supplier>() { + @Override + public Maybe get() throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + } + ) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void maybeCombiner() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + TestObserver to = Maybe.just(1) + .subscribeOn(Schedulers.io()) + .flatMap(new Function>() { + @Override + public Maybe apply(Integer v) throws Exception { + sleep(); + return Maybe.error(new TestException()); + } + }, (a, b) -> a + b) + .test(); + + cb.await(); + + beforeCancelSleep(to); + + to.dispose(); + + Thread.sleep(SLEEP_AFTER_CANCEL); + + to.assertEmpty(); + + assertTrue(errors.toString(), errors.isEmpty()); + } finally { + RxJavaPlugins.reset(); + } + } + @Test public void maybeCompletable() throws Exception { List errors = TestHelper.trackPluginErrors(); diff --git a/src/test/java/io/reactivex/rxjava3/disposables/CompositeDisposableTest.java b/src/test/java/io/reactivex/rxjava3/disposables/CompositeDisposableTest.java index 2d6fec394b..3f76d283d7 100644 --- a/src/test/java/io/reactivex/rxjava3/disposables/CompositeDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/disposables/CompositeDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public class CompositeDisposableTest extends RxJavaTest { public void success() { final AtomicInteger counter = new AtomicInteger(); CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -42,7 +42,7 @@ public void run() { })); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -63,7 +63,7 @@ public void shouldUnsubscribeAll() throws InterruptedException { final int count = 10; final CountDownLatch start = new CountDownLatch(1); for (int i = 0; i < count; i++) { - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -72,7 +72,7 @@ public void run() { })); } - final List threads = new ArrayList(); + final List threads = new ArrayList<>(); for (int i = 0; i < count; i++) { final Thread t = new Thread() { @Override @@ -101,7 +101,7 @@ public void run() { public void exception() { final AtomicInteger counter = new AtomicInteger(); CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -110,7 +110,7 @@ public void run() { })); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -135,7 +135,7 @@ public void run() { public void compositeException() { final AtomicInteger counter = new AtomicInteger(); CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -144,7 +144,7 @@ public void run() { })); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -152,7 +152,7 @@ public void run() { } })); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -175,8 +175,8 @@ public void run() { @Test public void removeUnsubscribes() { - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); CompositeDisposable cd = new CompositeDisposable(); cd.add(d1); @@ -190,8 +190,8 @@ public void removeUnsubscribes() { @Test public void clear() { - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); CompositeDisposable cd = new CompositeDisposable(); cd.add(d1); @@ -206,7 +206,7 @@ public void clear() { assertTrue(d2.isDisposed()); assertFalse(cd.isDisposed()); - Disposable d3 = Disposables.empty(); + Disposable d3 = Disposable.empty(); cd.add(d3); cd.dispose(); @@ -219,7 +219,7 @@ public void clear() { public void unsubscribeIdempotence() { final AtomicInteger counter = new AtomicInteger(); CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -244,7 +244,7 @@ public void unsubscribeIdempotenceConcurrently() final int count = 10; final CountDownLatch start = new CountDownLatch(1); - cd.add(Disposables.fromRunnable(new Runnable() { + cd.add(Disposable.fromRunnable(new Runnable() { @Override public void run() { @@ -253,7 +253,7 @@ public void run() { })); - final List threads = new ArrayList(); + final List threads = new ArrayList<>(); for (int i = 0; i < count; i++) { final Thread t = new Thread() { @Override @@ -290,7 +290,7 @@ public void tryRemoveIfNotIn() { cd.remove(cd1); cd.add(cd2); - cd.remove(cd1); // try removing agian + cd.remove(cd1); // try removing again } @Test(expected = NullPointerException.class) @@ -301,8 +301,8 @@ public void addingNullDisposableIllegal() { @Test public void initializeVarargs() { - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); CompositeDisposable cd = new CompositeDisposable(d1, d2); @@ -315,8 +315,8 @@ public void initializeVarargs() { assertTrue(d1.isDisposed()); assertTrue(d2.isDisposed()); - Disposable d3 = Disposables.empty(); - Disposable d4 = Disposables.empty(); + Disposable d3 = Disposable.empty(); + Disposable d4 = Disposable.empty(); cd = new CompositeDisposable(d3, d4); @@ -330,8 +330,8 @@ public void initializeVarargs() { @Test public void initializeIterable() { - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); CompositeDisposable cd = new CompositeDisposable(Arrays.asList(d1, d2)); @@ -344,8 +344,8 @@ public void initializeIterable() { assertTrue(d1.isDisposed()); assertTrue(d2.isDisposed()); - Disposable d3 = Disposables.empty(); - Disposable d4 = Disposables.empty(); + Disposable d3 = Disposable.empty(); + Disposable d4 = Disposable.empty(); cd = new CompositeDisposable(Arrays.asList(d3, d4)); @@ -363,9 +363,9 @@ public void initializeIterable() { public void addAll() { CompositeDisposable cd = new CompositeDisposable(); - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); - Disposable d3 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); + Disposable d3 = Disposable.empty(); cd.addAll(d1, d2); cd.addAll(d3); @@ -379,8 +379,8 @@ public void addAll() { assertTrue(d1.isDisposed()); assertTrue(d2.isDisposed()); - d1 = Disposables.empty(); - d2 = Disposables.empty(); + d1 = Disposable.empty(); + d2 = Disposable.empty(); cd = new CompositeDisposable(); @@ -406,14 +406,14 @@ public void addAfterDisposed() { CompositeDisposable cd = new CompositeDisposable(); cd.dispose(); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); assertFalse(cd.add(d1)); assertTrue(d1.isDisposed()); - d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); assertFalse(cd.addAll(d1, d2)); @@ -427,11 +427,11 @@ public void delete() { CompositeDisposable cd = new CompositeDisposable(); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); assertFalse(cd.delete(d1)); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); cd.add(d2); @@ -466,7 +466,7 @@ public void addRace() { Runnable run = new Runnable() { @Override public void run() { - cd.add(Disposables.empty()); + cd.add(Disposable.empty()); } }; @@ -482,7 +482,7 @@ public void addAllRace() { Runnable run = new Runnable() { @Override public void run() { - cd.addAll(Disposables.empty()); + cd.addAll(Disposable.empty()); } }; @@ -495,7 +495,7 @@ public void removeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -515,7 +515,7 @@ public void deleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -535,7 +535,7 @@ public void clearRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -565,7 +565,7 @@ public void run() { Runnable run2 = new Runnable() { @Override public void run() { - cd.add(Disposables.empty()); + cd.add(Disposable.empty()); } }; @@ -588,7 +588,7 @@ public void run() { Runnable run2 = new Runnable() { @Override public void run() { - cd.addAll(Disposables.empty()); + cd.addAll(Disposable.empty()); } }; @@ -601,7 +601,7 @@ public void removeDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -628,7 +628,7 @@ public void deleteDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -655,7 +655,7 @@ public void clearDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -682,7 +682,7 @@ public void sizeDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final CompositeDisposable cd = new CompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -708,14 +708,14 @@ public void run() { public void disposeThrowsIAE() { CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromAction(new Action() { + cd.add(Disposable.fromAction(new Action() { @Override public void run() throws Exception { throw new IllegalArgumentException(); } })); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); cd.add(d1); @@ -733,14 +733,14 @@ public void run() throws Exception { public void disposeThrowsError() { CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromAction(new Action() { + cd.add(Disposable.fromAction(new Action() { @Override public void run() throws Exception { throw new AssertionError(); } })); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); cd.add(d1); @@ -758,14 +758,14 @@ public void run() throws Exception { public void disposeThrowsCheckedException() { CompositeDisposable cd = new CompositeDisposable(); - cd.add(Disposables.fromAction(new Action() { + cd.add(Disposable.fromAction(new Action() { @Override public void run() throws Exception { throw new IOException(); } })); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); cd.add(d1); @@ -804,7 +804,7 @@ public boolean isDisposed() { } }); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); cd.add(d1); diff --git a/src/test/java/io/reactivex/rxjava3/disposables/DisposablesTest.java b/src/test/java/io/reactivex/rxjava3/disposables/DisposableTest.java similarity index 51% rename from src/test/java/io/reactivex/rxjava3/disposables/DisposablesTest.java rename to src/test/java/io/reactivex/rxjava3/disposables/DisposableTest.java index d5ea5d0d49..e541d404da 100644 --- a/src/test/java/io/reactivex/rxjava3/disposables/DisposablesTest.java +++ b/src/test/java/io/reactivex/rxjava3/disposables/DisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,25 +25,34 @@ import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.testsupport.TestHelper; -public class DisposablesTest extends RxJavaTest { +public class DisposableTest extends RxJavaTest { @Test public void unsubscribeOnlyOnce() { - Runnable dispose = mock(Runnable.class); - Disposable subscription = Disposables.fromRunnable(dispose); - subscription.dispose(); - subscription.dispose(); - verify(dispose, times(1)).run(); + Runnable run = mock(Runnable.class); + + Disposable d = Disposable.fromRunnable(run); + + assertTrue(d.toString(), d.toString().contains("RunnableDisposable(disposed=false, ")); + + d.dispose(); + assertTrue(d.toString(), d.toString().contains("RunnableDisposable(disposed=true, ")); + + d.dispose(); + assertTrue(d.toString(), d.toString().contains("RunnableDisposable(disposed=true, ")); + + verify(run, times(1)).run(); } @Test public void empty() { - Disposable empty = Disposables.empty(); + Disposable empty = Disposable.empty(); assertFalse(empty.isDisposed()); empty.dispose(); assertTrue(empty.isDisposed()); @@ -51,38 +60,31 @@ public void empty() { @Test public void unsubscribed() { - Disposable disposed = Disposables.disposed(); + Disposable disposed = Disposable.disposed(); assertTrue(disposed.isDisposed()); } @Test - public void utilityClass() { - TestHelper.checkUtilityClass(Disposables.class); - } + public void fromAction() throws Throwable { + Action action = mock(Action.class); - @Test - public void fromAction() { - class AtomicAction extends AtomicBoolean implements Action { + Disposable d = Disposable.fromAction(action); - private static final long serialVersionUID = -1517510584253657229L; + assertTrue(d.toString(), d.toString().contains("ActionDisposable(disposed=false, ")); - @Override - public void run() throws Exception { - set(true); - } - } + d.dispose(); + assertTrue(d.toString(), d.toString().contains("ActionDisposable(disposed=true, ")); - AtomicAction aa = new AtomicAction(); + d.dispose(); + assertTrue(d.toString(), d.toString().contains("ActionDisposable(disposed=true, ")); - Disposables.fromAction(aa).dispose(); - - assertTrue(aa.get()); + verify(action, times(1)).run(); } @Test public void fromActionThrows() { try { - Disposables.fromAction(new Action() { + Disposable.fromAction(new Action() { @Override public void run() throws Exception { throw new IllegalArgumentException(); @@ -94,7 +96,7 @@ public void run() throws Exception { } try { - Disposables.fromAction(new Action() { + Disposable.fromAction(new Action() { @Override public void run() throws Exception { throw new InternalError(); @@ -106,7 +108,7 @@ public void run() throws Exception { } try { - Disposables.fromAction(new Action() { + Disposable.fromAction(new Action() { @Override public void run() throws Exception { throw new IOException(); @@ -125,7 +127,7 @@ public void run() throws Exception { @Test public void disposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Runnable r = new Runnable() { @Override @@ -140,14 +142,14 @@ public void run() { @Test(expected = NullPointerException.class) public void fromSubscriptionNull() { - Disposables.fromSubscription(null); + Disposable.fromSubscription(null); } @Test public void fromSubscription() { Subscription s = mock(Subscription.class); - Disposables.fromSubscription(s).dispose(); + Disposable.fromSubscription(s).dispose(); verify(s).cancel(); verify(s, never()).request(anyInt()); @@ -158,12 +160,12 @@ public void setOnceTwice() { List errors = TestHelper.trackPluginErrors(); try { - AtomicReference target = new AtomicReference(); - Disposable d = Disposables.empty(); + AtomicReference target = new AtomicReference<>(); + Disposable d = Disposable.empty(); DisposableHelper.setOnce(target, d); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); DisposableHelper.setOnce(target, d1); @@ -174,4 +176,79 @@ public void setOnceTwice() { RxJavaPlugins.reset(); } } + + @Test + public void fromAutoCloseable() { + AtomicInteger counter = new AtomicInteger(); + + AutoCloseable ac = () -> counter.getAndIncrement(); + + Disposable d = Disposable.fromAutoCloseable(ac); + + assertFalse(d.isDisposed()); + assertEquals(0, counter.get()); + assertTrue(d.toString(), d.toString().contains("AutoCloseableDisposable(disposed=false, ")); + + d.dispose(); + + assertTrue(d.isDisposed()); + assertEquals(1, counter.get()); + assertTrue(d.toString(), d.toString().contains("AutoCloseableDisposable(disposed=true, ")); + + d.dispose(); + + assertTrue(d.isDisposed()); + assertEquals(1, counter.get()); + assertTrue(d.toString(), d.toString().contains("AutoCloseableDisposable(disposed=true, ")); + } + + @Test + public void fromAutoCloseableThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + AutoCloseable ac = () -> { throw new TestException(); }; + + Disposable d = Disposable.fromAutoCloseable(ac); + + assertFalse(d.isDisposed()); + + assertTrue(errors.isEmpty()); + + try { + d.dispose(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + + assertTrue(d.isDisposed()); + + d.dispose(); + + assertTrue(d.isDisposed()); + + assertTrue(errors.isEmpty()); + }); + } + + @Test + public void toAutoCloseable() throws Exception { + AtomicInteger counter = new AtomicInteger(); + + Disposable d = Disposable.fromAction(() -> counter.getAndIncrement()); + + AutoCloseable ac = Disposable.toAutoCloseable(d); + + assertFalse(d.isDisposed()); + assertEquals(0, counter.get()); + + ac.close(); + + assertTrue(d.isDisposed()); + assertEquals(1, counter.get()); + + ac.close(); + + assertTrue(d.isDisposed()); + assertEquals(1, counter.get()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/disposables/FutureDisposableTest.java b/src/test/java/io/reactivex/rxjava3/disposables/FutureDisposableTest.java index 7cb13de5a8..544e39ad1b 100644 --- a/src/test/java/io/reactivex/rxjava3/disposables/FutureDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/disposables/FutureDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,8 +26,8 @@ public class FutureDisposableTest extends RxJavaTest { @Test public void normal() { - FutureTask ft = new FutureTask(Functions.EMPTY_RUNNABLE, null); - Disposable d = Disposables.fromFuture(ft); + FutureTask ft = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); + Disposable d = Disposable.fromFuture(ft); assertFalse(d.isDisposed()); d.dispose(); @@ -43,8 +43,8 @@ public void normal() { @Test public void interruptible() { - FutureTask ft = new FutureTask(Functions.EMPTY_RUNNABLE, null); - Disposable d = Disposables.fromFuture(ft, true); + FutureTask ft = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); + Disposable d = Disposable.fromFuture(ft, true); assertFalse(d.isDisposed()); d.dispose(); @@ -60,7 +60,7 @@ public void interruptible() { @Test public void normalDone() { - FutureTask ft = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask ft = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); FutureDisposable d = new FutureDisposable(ft, false); assertFalse(d.isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/disposables/SequentialDisposableTest.java b/src/test/java/io/reactivex/rxjava3/disposables/SequentialDisposableTest.java index 05f544bfa3..3066343301 100644 --- a/src/test/java/io/reactivex/rxjava3/disposables/SequentialDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/disposables/SequentialDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -56,7 +56,7 @@ public void notDisposedWhenReplaced() { final Disposable underlying = mock(Disposable.class); serialDisposable.update(underlying); - serialDisposable.replace(Disposables.empty()); + serialDisposable.replace(Disposable.empty()); serialDisposable.dispose(); verify(underlying, never()).dispose(); @@ -129,7 +129,7 @@ public void settingUnderlyingWhenUnsubscribedCausesImmediateUnsubscriptionConcur final int count = 10; final CountDownLatch end = new CountDownLatch(count); - final List threads = new ArrayList(); + final List threads = new ArrayList<>(); for (int i = 0; i < count; i++) { final Thread t = new Thread() { @Override @@ -164,12 +164,12 @@ public void run() { public void concurrentSetDisposableShouldNotInterleave() throws InterruptedException { final int count = 10; - final List subscriptions = new ArrayList(); + final List subscriptions = new ArrayList<>(); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(count); - final List threads = new ArrayList(); + final List threads = new ArrayList<>(); for (int i = 0; i < count; i++) { final Disposable subscription = mock(Disposable.class); subscriptions.add(subscription); diff --git a/src/test/java/io/reactivex/rxjava3/disposables/SerialDisposableTests.java b/src/test/java/io/reactivex/rxjava3/disposables/SerialDisposableTests.java index 9ab98dae94..3a2e73582b 100644 --- a/src/test/java/io/reactivex/rxjava3/disposables/SerialDisposableTests.java +++ b/src/test/java/io/reactivex/rxjava3/disposables/SerialDisposableTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -56,7 +56,7 @@ public void notDisposedWhenReplaced() { final Disposable underlying = mock(Disposable.class); serialDisposable.set(underlying); - serialDisposable.replace(Disposables.empty()); + serialDisposable.replace(Disposable.empty()); serialDisposable.dispose(); verify(underlying, never()).dispose(); @@ -129,7 +129,7 @@ public void settingUnderlyingWhenUnsubscribedCausesImmediateUnsubscriptionConcur final int count = 10; final CountDownLatch end = new CountDownLatch(count); - final List threads = new ArrayList(); + final List threads = new ArrayList<>(); for (int i = 0; i < count; i++) { final Thread t = new Thread() { @Override @@ -164,12 +164,12 @@ public void run() { public void concurrentSetDisposableShouldNotInterleave() throws InterruptedException { final int count = 10; - final List subscriptions = new ArrayList(); + final List subscriptions = new ArrayList<>(); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(count); - final List threads = new ArrayList(); + final List threads = new ArrayList<>(); for (int i = 0; i < count; i++) { final Disposable subscription = mock(Disposable.class); subscriptions.add(subscription); @@ -206,7 +206,7 @@ public void run() { @Test public void disposeState() { - Disposable empty = Disposables.empty(); + Disposable empty = Disposable.empty(); SerialDisposable d = new SerialDisposable(empty); assertFalse(d.isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java b/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java index 6f8ab6ff55..cc34f62b89 100644 --- a/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/exceptions/CompositeExceptionTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.exceptions; import static org.junit.Assert.*; @@ -31,7 +29,7 @@ public class CompositeExceptionTest extends RxJavaTest { private final Throwable ex3 = new Throwable("Ex3", ex2); private CompositeException getNewCompositeExceptionWithEx123() { - List throwables = new ArrayList(); + List throwables = new ArrayList<>(); throwables.add(ex1); throwables.add(ex2); throwables.add(ex3); @@ -65,7 +63,7 @@ public void emptyErrors() { assertEquals("errors is empty", e.getMessage()); } try { - new CompositeException(new ArrayList()); + new CompositeException(new ArrayList<>()); fail("CompositeException should fail if errors is empty"); } catch (IllegalArgumentException e) { assertEquals("errors is empty", e.getMessage()); @@ -134,7 +132,7 @@ public void compositeExceptionFromCompositeAndChild() { @Test public void compositeExceptionFromTwoDuplicateComposites() { - List exs = new ArrayList(); + List exs = new ArrayList<>(); exs.add(getNewCompositeExceptionWithEx123()); exs.add(getNewCompositeExceptionWithEx123()); CompositeException cex = new CompositeException(exs); @@ -150,7 +148,7 @@ public void compositeExceptionFromTwoDuplicateComposites() { cex.getCause().printStackTrace(); } - /** + /* * This hijacks the Throwable.printStackTrace() output and puts it in a string, where we can look for * "CIRCULAR REFERENCE" (a String added by Throwable.printEnclosedStackTrace) */ diff --git a/src/test/java/io/reactivex/rxjava3/exceptions/ExceptionsTest.java b/src/test/java/io/reactivex/rxjava3/exceptions/ExceptionsTest.java index 3d2e1eeec4..b6aa069010 100644 --- a/src/test/java/io/reactivex/rxjava3/exceptions/ExceptionsTest.java +++ b/src/test/java/io/reactivex/rxjava3/exceptions/ExceptionsTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.exceptions; import static org.junit.Assert.*; diff --git a/src/test/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedExceptionTest.java b/src/test/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedExceptionTest.java index b8a78de7dc..70ba775bc1 100644 --- a/src/test/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedExceptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/exceptions/OnErrorNotImplementedExceptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/exceptions/TestException.java b/src/test/java/io/reactivex/rxjava3/exceptions/TestException.java index 8618c7df5e..7bcdd318fd 100644 --- a/src/test/java/io/reactivex/rxjava3/exceptions/TestException.java +++ b/src/test/java/io/reactivex/rxjava3/exceptions/TestException.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,7 +14,7 @@ package io.reactivex.rxjava3.exceptions; /** - * Exception for testing if unchecked expections propagate as-is without confusing with + * Exception for testing if unchecked exceptions propagate as-is without confusing with * other type of common exceptions. */ public final class TestException extends RuntimeException { diff --git a/src/test/java/io/reactivex/rxjava3/flowable/Burst.java b/src/test/java/io/reactivex/rxjava3/flowable/Burst.java index 8acf477242..efa0993042 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/Burst.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/Burst.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.flowable; import java.util.*; @@ -52,18 +53,18 @@ protected void subscribeActual(final Subscriber subscriber) { } - @SuppressWarnings("unchecked") public static Builder item(T item) { return items(item); } + @SafeVarargs public static Builder items(T... items) { - return new Builder(Arrays.asList(items)); + return new Builder<>(Arrays.asList(items)); } final class BurstSubscription implements Subscription { private final Subscriber subscriber; - final Queue q = new ConcurrentLinkedQueue(items); + final Queue q = new ConcurrentLinkedQueue<>(items); final AtomicLong requested = new AtomicLong(); volatile boolean cancelled; @@ -121,7 +122,7 @@ public Flowable error(Throwable e) { } public Flowable create() { - return new Burst(error, items); + return new Burst<>(error, items); } } diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableBackpressureTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableBackpressureTests.java index e110b410c2..e6b3a02c68 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableBackpressureTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableBackpressureTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; +import io.reactivex.rxjava3.exceptions.QueueOverflowException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; import io.reactivex.rxjava3.internal.util.BackpressureHelper; @@ -82,7 +82,7 @@ public void doAfterTest() { public void observeOn() { int num = (int) (Flowable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); incrementingIntegers(c).observeOn(Schedulers.computation()).take(num).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertNoErrors(); @@ -95,7 +95,7 @@ public void observeOn() { public void observeOnWithSlowConsumer() { int num = (int) (Flowable.bufferSize() * 0.2); AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); incrementingIntegers(c).observeOn(Schedulers.computation()).map( new Function() { @Override @@ -121,7 +121,7 @@ public void mergeSync() { int num = (int) (Flowable.bufferSize() * 4.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable merged = Flowable.merge(incrementingIntegers(c1), incrementingIntegers(c2)); merged.take(num).subscribe(ts); @@ -132,7 +132,7 @@ public void mergeSync() { assertEquals(num, ts.values().size()); // either one can starve the other, but neither should be capable of doing more than 5 batches (taking 4.1) // TODO is it possible to make this deterministic rather than one possibly starving the other? - // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" algoritms generally take a performance hit + // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" algorithms generally take a performance hit assertTrue(c1.get() < Flowable.bufferSize() * 5); assertTrue(c2.get() < Flowable.bufferSize() * 5); } @@ -142,7 +142,7 @@ public void mergeAsync() { int num = (int) (Flowable.bufferSize() * 4.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable merged = Flowable.merge( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation())); @@ -154,7 +154,7 @@ public void mergeAsync() { assertEquals(num, ts.values().size()); // either one can starve the other, but neither should be capable of doing more than 5 batches (taking 4.1) // TODO is it possible to make this deterministic rather than one possibly starving the other? - // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" algoritms generally take a performance hit + // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" algorithms generally take a performance hit int max = Flowable.bufferSize() * 7; assertTrue("" + c1.get() + " >= " + max, c1.get() < max); assertTrue("" + c2.get() + " >= " + max, c2.get() < max); @@ -171,7 +171,7 @@ public void mergeAsyncThenObserveOnLoop() { AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable merged = Flowable.merge( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation())); @@ -194,7 +194,7 @@ public void mergeAsyncThenObserveOn() { int num = (int) (Flowable.bufferSize() * 4.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable merged = Flowable.merge( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation())); @@ -206,7 +206,7 @@ public void mergeAsyncThenObserveOn() { assertEquals(num, ts.values().size()); // either one can starve the other, but neither should be capable of doing more than 5 batches (taking 4.1) // TODO is it possible to make this deterministic rather than one possibly starving the other? - // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" algoritms generally take a performance hit + // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" algorithms generally take a performance hit // akarnokd => run this in a loop over 10k times and never saw values get as high as 7*SIZE, but since observeOn delays the unsubscription non-deterministically, the test will remain unreliable assertTrue(c1.get() < Flowable.bufferSize() * 7); assertTrue(c2.get() < Flowable.bufferSize() * 7); @@ -216,7 +216,7 @@ public void mergeAsyncThenObserveOn() { public void flatMapSync() { int num = (int) (Flowable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); incrementingIntegers(c) .flatMap(new Function>() { @@ -240,7 +240,7 @@ public void zipSync() { int num = (int) (Flowable.bufferSize() * 4.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable zipped = Flowable.zip( incrementingIntegers(c1), @@ -268,7 +268,7 @@ public void zipAsync() { int num = (int) (Flowable.bufferSize() * 2.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable zipped = Flowable.zip( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation()), @@ -295,8 +295,8 @@ public void subscribeOnScheduling() { for (int i = 0; i < 100; i++) { int num = (int) (Flowable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); - ConcurrentLinkedQueue threads = new ConcurrentLinkedQueue(); - TestSubscriber ts = new TestSubscriber(); + ConcurrentLinkedQueue threads = new ConcurrentLinkedQueue<>(); + TestSubscriber ts = new TestSubscriber<>(); // observeOn is there to make it async and need backpressure incrementingIntegers(c, threads).subscribeOn(Schedulers.computation()).observeOn(Schedulers.computation()).take(num).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -325,7 +325,7 @@ public void subscribeOnScheduling() { public void takeFilterSkipChainAsync() { int num = (int) (Flowable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); incrementingIntegers(c).observeOn(Schedulers.computation()) .skip(10000) .filter(new Predicate() { @@ -452,7 +452,7 @@ public void onNext(Integer t) { @Test public void firehoseFailsAsExpected() { AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose(c).observeOn(Schedulers.computation()) .map(new Function() { @@ -475,7 +475,7 @@ public Integer apply(Integer v) { int vc = ts.values().size(); assertTrue("10 < " + vc, vc <= 10); - ts.assertError(MissingBackpressureException.class); + ts.assertError(QueueOverflowException.class); } @Test @@ -496,7 +496,7 @@ public void onBackpressureDrop() { } int num = (int) (Flowable.bufferSize() * 1.1); // > 1 so that take doesn't prevent buffer overflow AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose(c).onBackpressureDrop() .observeOn(Schedulers.computation()) .map(SLOW_PASS_THRU).take(num).subscribe(ts); @@ -521,7 +521,7 @@ public void onBackpressureDropWithAction() { final AtomicInteger dropCount = new AtomicInteger(); final AtomicInteger passCount = new AtomicInteger(); final int num = Flowable.bufferSize() * 3; // > 1 so that take doesn't prevent buffer overflow - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose(emitCount) .onBackpressureDrop(new Consumer() { @@ -561,7 +561,7 @@ public void onBackpressureDropSynchronous() { for (int i = 0; i < 100; i++) { int num = (int) (Flowable.bufferSize() * 1.1); // > 1 so that take doesn't prevent buffer overflow AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose(c).onBackpressureDrop() .map(SLOW_PASS_THRU).take(num).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -584,7 +584,7 @@ public void onBackpressureDropSynchronousWithAction() { final AtomicInteger dropCount = new AtomicInteger(); int num = (int) (Flowable.bufferSize() * 1.1); // > 1 so that take doesn't prevent buffer overflow AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose(c).onBackpressureDrop(new Consumer() { @Override public void accept(Integer j) { @@ -613,7 +613,7 @@ public void accept(Integer j) { public void onBackpressureBuffer() { int num = (int) (Flowable.bufferSize() * 1.1); // > 1 so that take doesn't prevent buffer overflow AtomicInteger c = new AtomicInteger(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose(c).takeWhile(new Predicate() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableCollectTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableCollectTest.java index f0f93c6453..7077995982 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableCollectTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableCollectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public void collectToListFlowable() { .collect(new Supplier>() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -109,7 +109,7 @@ public void accept(List list, Integer t) { @Test public void collectorFailureDoesNotResultInTwoErrorEmissionsFlowable() { try { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); RxJavaPlugins.setErrorHandler(addToList(list)); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); @@ -167,11 +167,10 @@ public void accept(Object o, Integer t) { assertFalse(added.get()); } - @SuppressWarnings("unchecked") @Test public void collectIntoFlowable() { Flowable.just(1, 1, 1, 1, 2) - .collectInto(new HashSet(), new BiConsumer, Integer>() { + .collectInto(new HashSet<>(), new BiConsumer, Integer>() { @Override public void accept(HashSet s, Integer v) throws Exception { s.add(v); @@ -179,7 +178,7 @@ public void accept(HashSet s, Integer v) throws Exception { }) .toFlowable() .test() - .assertResult(new HashSet(Arrays.asList(1, 2))); + .assertResult(new HashSet<>(Arrays.asList(1, 2))); } @Test @@ -188,7 +187,7 @@ public void collectToList() { .collect(new Supplier>() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -261,7 +260,7 @@ public void accept(List list, Integer t) { @Test public void collectorFailureDoesNotResultInTwoErrorEmissions() { try { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); RxJavaPlugins.setErrorHandler(addToList(list)); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); @@ -316,24 +315,23 @@ public void accept(Object o, Integer t) { assertFalse(added.get()); } - @SuppressWarnings("unchecked") @Test public void collectInto() { Flowable.just(1, 1, 1, 1, 2) - .collectInto(new HashSet(), new BiConsumer, Integer>() { + .collectInto(new HashSet<>(), new BiConsumer, Integer>() { @Override public void accept(HashSet s, Integer v) throws Exception { s.add(v); } }) .test() - .assertResult(new HashSet(Arrays.asList(1, 2))); + .assertResult(new HashSet<>(Arrays.asList(1, 2))); } @Test public void dispose() { TestHelper.checkDisposed(Flowable.just(1, 2) - .collect(Functions.justSupplier(new ArrayList()), new BiConsumer, Integer>() { + .collect(Functions.justSupplier(new ArrayList<>()), new BiConsumer, Integer>() { @Override public void accept(ArrayList a, Integer b) throws Exception { a.add(b); @@ -341,7 +339,7 @@ public void accept(ArrayList a, Integer b) throws Exception { })); TestHelper.checkDisposed(Flowable.just(1, 2) - .collect(Functions.justSupplier(new ArrayList()), new BiConsumer, Integer>() { + .collect(Functions.justSupplier(new ArrayList<>()), new BiConsumer, Integer>() { @Override public void accept(ArrayList a, Integer b) throws Exception { a.add(b); @@ -354,7 +352,7 @@ public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowable(new Function, Flowable>>() { @Override public Flowable> apply(Flowable f) throws Exception { - return f.collect(Functions.justSupplier(new ArrayList()), + return f.collect(Functions.justSupplier(new ArrayList<>()), new BiConsumer, Integer>() { @Override public void accept(ArrayList a, Integer b) throws Exception { @@ -366,7 +364,7 @@ public void accept(ArrayList a, Integer b) throws Exception { TestHelper.checkDoubleOnSubscribeFlowableToSingle(new Function, Single>>() { @Override public Single> apply(Flowable f) throws Exception { - return f.collect(Functions.justSupplier(new ArrayList()), + return f.collect(Functions.justSupplier(new ArrayList<>()), new BiConsumer, Integer>() { @Override public void accept(ArrayList a, Integer b) throws Exception { diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableCombineLatestTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableCombineLatestTests.java index 3adac790ae..107c5e583f 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableCombineLatestTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableCombineLatestTests.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.flowable; import org.junit.Test; diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableConcatTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableConcatTests.java index e800a9173c..5efb4195e6 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableConcatTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableConcatTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.flowable; import static org.junit.Assert.assertEquals; @@ -61,7 +62,6 @@ public void concatWithIterableOfFlowable() { Flowable f2 = Flowable.just("three", "four"); Flowable f3 = Flowable.just("five", "six"); - @SuppressWarnings("unchecked") Iterable> is = Arrays.asList(f1, f2, f3); List values = Flowable.concat(Flowable.fromIterable(is)).toList().blockingGet(); diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableConversionTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableConversionTest.java index 23b484e369..7079f833f2 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableConversionTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableConversionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,7 +44,7 @@ public static class CylonDetectorObservable { protected Publisher onSubscribe; public static CylonDetectorObservable create(Publisher onSubscribe) { - return new CylonDetectorObservable(onSubscribe); + return new CylonDetectorObservable<>(onSubscribe); } protected CylonDetectorObservable(Publisher onSubscribe) { @@ -56,10 +56,10 @@ public void subscribe(Subscriber subscriber) { } public CylonDetectorObservable lift(FlowableOperator operator) { - return x(new RobotConversionFunc(operator)); + return x(new RobotConversionFunc<>(operator)); } - public O x(Function, O> operator) { + public O x(Function, O> operator) { try { return operator.apply(onSubscribe); } catch (Throwable ex) { @@ -76,11 +76,11 @@ public CylonDetectorObservable compose(Function beep(Predicate predicate) { - return new CylonDetectorObservable(new FlowableFilter(Flowable.fromPublisher(onSubscribe), predicate)); + return new CylonDetectorObservable<>(new FlowableFilter<>(Flowable.fromPublisher(onSubscribe), predicate)); } public final CylonDetectorObservable boop(Function func) { - return new CylonDetectorObservable(new FlowableMap(Flowable.fromPublisher(onSubscribe), func)); + return new CylonDetectorObservable<>(new FlowableMap<>(Flowable.fromPublisher(onSubscribe), func)); } public CylonDetectorObservable DESTROY() { @@ -147,7 +147,7 @@ public Flowable apply(final Publisher onSubscribe) { @Test public void conversionBetweenObservableClasses() { - final TestObserver to = new TestObserver(new DefaultObserver() { + final TestObserver to = new TestObserver<>(new DefaultObserver() { @Override public void onComplete() { @@ -175,7 +175,7 @@ public void accept(Object pv) { System.out.println(pv); } }) - .to(new ConvertToCylonDetector()) + .to(new ConvertToCylonDetector<>()) .beep(new Predicate() { @Override public boolean test(Object t) { @@ -189,7 +189,7 @@ public Object apply(Object cylon) { } }) .DESTROY() - .x(new ConvertToObservable()) + .x(new ConvertToObservable<>()) .reduce("Cylon Detector finished. Report:\n", new BiFunction() { @Override public String apply(String a, String n) { @@ -204,7 +204,7 @@ public String apply(String a, String n) { @Test public void convertToConcurrentQueue() { - final AtomicReference thrown = new AtomicReference(null); + final AtomicReference thrown = new AtomicReference<>(null); final AtomicBoolean isFinished = new AtomicBoolean(false); ConcurrentLinkedQueue queue = Flowable.range(0, 5) .flatMap(new Function>() { @@ -228,7 +228,7 @@ public Integer apply(Integer k) { .to(new FlowableConverter>() { @Override public ConcurrentLinkedQueue apply(Flowable onSubscribe) { - final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(); + final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue<>(); onSubscribe.subscribe(new DefaultSubscriber() { @Override public void onComplete() { diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableCovarianceTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableCovarianceTest.java index a14462e905..9d7ca31319 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableCovarianceTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableCovarianceTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.flowable; @@ -66,7 +63,7 @@ public int compare(Media t1, Media t2) { @Test public void groupByCompose() { Flowable movies = Flowable.just(new HorrorMovie(), new ActionMovie(), new Movie()); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); movies .groupBy(new Function() { @@ -188,9 +185,9 @@ public Flowable apply(List> listOfLists) { } else { // diff the two List newList = listOfLists.get(1); - List oldList = new ArrayList(listOfLists.get(0)); + List oldList = new ArrayList<>(listOfLists.get(0)); - Set delta = new LinkedHashSet(); + Set delta = new LinkedHashSet<>(); delta.addAll(newList); // remove all that match in old delta.removeAll(oldList); @@ -212,7 +209,7 @@ public Flowable apply(List> listOfLists) { @Override public Publisher apply(Flowable> movieList) { return movieList - .startWithItem(new ArrayList()) + .startWithItem(new ArrayList<>()) .buffer(2, 1) .skip(1) .flatMap(calculateDelta); diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoAfterNextTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoAfterNextTest.java index 58f21b10d3..eb6878edbd 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoAfterNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoAfterNextTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.flowable; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoOnTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoOnTest.java index 2655f8c4fd..f4ff588d53 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableDoOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class FlowableDoOnTest extends RxJavaTest { @Test public void doOnEach() { - final AtomicReference r = new AtomicReference(); + final AtomicReference r = new AtomicReference<>(); String output = Flowable.just("one").doOnNext(new Consumer() { @Override public void accept(String v) { @@ -41,7 +41,7 @@ public void accept(String v) { @Test public void doOnError() { - final AtomicReference r = new AtomicReference(); + final AtomicReference r = new AtomicReference<>(); Throwable t = null; try { Flowable. error(new RuntimeException("an error")) diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableErrorHandlingTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableErrorHandlingTests.java index 694dedded4..e15c7a1dec 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableErrorHandlingTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableErrorHandlingTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public class FlowableErrorHandlingTests extends RxJavaTest { @Test public void onNextError() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference caughtError = new AtomicReference(); + final AtomicReference caughtError = new AtomicReference<>(); Flowable f = Flowable.interval(50, TimeUnit.MILLISECONDS); Subscriber subscriber = new DefaultSubscriber() { @@ -72,7 +72,7 @@ public void onNext(Long args) { @Test public void onNextErrorAcrossThread() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference caughtError = new AtomicReference(); + final AtomicReference caughtError = new AtomicReference<>(); Flowable f = Flowable.interval(50, TimeUnit.MILLISECONDS); Subscriber subscriber = new DefaultSubscriber() { diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStream.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStream.java index 7c64da9ada..2428d6cb04 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStream.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStream.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public static Flowable getEventStream(final String type, final int numIns } public static Event randomEvent(String type, int numInstances) { - Map values = new LinkedHashMap(); + Map values = new LinkedHashMap<>(); values.put("count200", randomIntFrom0to(4000)); values.put("count4xx", randomIntFrom0to(300)); values.put("count5xx", randomIntFrom0to(500)); diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStreamTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStreamTest.java index 8965376b8a..dd38b88afc 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStreamTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableEventStreamTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.flowable; diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableFuseableTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableFuseableTest.java index 02fa0e5be7..7bf54e8aba 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableFuseableTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableFuseableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.flowable; import java.util.Arrays; @@ -17,7 +18,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.TestHelper; public class FlowableFuseableTest extends RxJavaTest { diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableGroupByTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableGroupByTests.java index b7e4edcab4..e37934eadd 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableGroupByTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableGroupByTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableMergeTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableMergeTests.java index e200cd0f12..d549c3fbb4 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableMergeTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableMergeTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableNotificationTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableNotificationTest.java index 2e2b95bbd8..08d1994fcb 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableNotificationTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableNotificationTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableNullTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableNullTests.java index ed520c494c..748a12d3c3 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableNullTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableNullTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,22 +42,11 @@ public class FlowableNullTests extends RxJavaTest { // Static methods //*********************************************************** - @Test(expected = NullPointerException.class) - public void ambVarargsNull() { - Flowable.ambArray((Publisher[])null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void ambVarargsOneIsNull() { Flowable.ambArray(Flowable.never(), null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void ambIterableNull() { - Flowable.amb((Iterable>)null); - } - @Test public void ambIterableIteratorNull() { Flowable.amb(new Iterable>() { @@ -68,7 +57,6 @@ public Iterator> iterator() { }).test().assertError(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void ambIterableOneIsNull() { Flowable.amb(Arrays.asList(Flowable.never(), null)) @@ -76,16 +64,6 @@ public void ambIterableOneIsNull() { .assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void combineLatestIterableNull() { - Flowable.combineLatestDelayError((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void combineLatestIterableIteratorNull() { Flowable.combineLatestDelayError(new Iterable>() { @@ -101,7 +79,6 @@ public Object apply(Object[] v) { }).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestIterableOneIsNull() { Flowable.combineLatestDelayError(Arrays.asList(Flowable.never(), null), new Function() { @@ -112,13 +89,6 @@ public Object apply(Object[] v) { }).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void combineLatestIterableFunctionNull() { - Flowable.combineLatestDelayError(Arrays.asList(just1), null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestIterableFunctionReturnsNull() { Flowable.combineLatestDelayError(Arrays.asList(just1), new Function() { @@ -129,11 +99,6 @@ public Object apply(Object[] v) { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void concatIterableNull() { - Flowable.concat((Iterable>)null); - } - @Test(expected = NullPointerException.class) public void concatIterableIteratorNull() { Flowable.concat(new Iterable>() { @@ -144,39 +109,16 @@ public Iterator> iterator() { }).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void concatIterableOneIsNull() { Flowable.concat(Arrays.asList(just1, null)).blockingLast(); } - @Test(expected = NullPointerException.class) - public void concatPublisherNull() { - Flowable.concat((Publisher>)null); - - } - - @Test(expected = NullPointerException.class) - public void concatArrayNull() { - Flowable.concatArray((Publisher[])null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void concatArrayOneIsNull() { Flowable.concatArray(just1, null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void createNull() { - Flowable.unsafeCreate(null); - } - - @Test(expected = NullPointerException.class) - public void deferFunctionNull() { - Flowable.defer(null); - } - @Test(expected = NullPointerException.class) public void deferFunctionReturnsNull() { Flowable.defer(new Supplier>() { @@ -187,11 +129,6 @@ public Publisher get() { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void errorFunctionNull() { - Flowable.error((Supplier)null); - } - @Test(expected = NullPointerException.class) public void errorFunctionReturnsNull() { Flowable.error(new Supplier() { @@ -202,26 +139,11 @@ public Throwable get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void errorThrowableNull() { - Flowable.error((Throwable)null); - } - - @Test(expected = NullPointerException.class) - public void fromArrayNull() { - Flowable.fromArray((Object[])null); - } - @Test(expected = NullPointerException.class) public void fromArrayOneIsNull() { Flowable.fromArray(1, null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromCallableNull() { - Flowable.fromCallable(null); - } - @Test(expected = NullPointerException.class) public void fromCallableReturnsNull() { Flowable.fromCallable(new Callable() { @@ -232,55 +154,25 @@ public Object call() throws Exception { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromFutureNull() { - Flowable.fromFuture(null); - } - @Test public void fromFutureReturnsNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); f.run(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.fromFuture(f).subscribe(ts); ts.assertNoValues(); ts.assertNotComplete(); ts.assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void fromFutureTimedFutureNull() { - Flowable.fromFuture(null, 1, TimeUnit.SECONDS); - } - - @Test(expected = NullPointerException.class) - public void fromFutureTimedUnitNull() { - Flowable.fromFuture(new FutureTask(Functions.EMPTY_RUNNABLE, null), 1, null); - } - - @Test(expected = NullPointerException.class) - public void fromFutureTimedSchedulerNull() { - Flowable.fromFuture(new FutureTask(Functions.EMPTY_RUNNABLE, null), 1, TimeUnit.SECONDS, null); - } - @Test(expected = NullPointerException.class) public void fromFutureTimedReturnsNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); f.run(); Flowable.fromFuture(f, 1, TimeUnit.SECONDS).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromFutureSchedulerNull() { - Flowable.fromFuture(new FutureTask(Functions.EMPTY_RUNNABLE, null), null); - } - - @Test(expected = NullPointerException.class) - public void fromIterableNull() { - Flowable.fromIterable(null); - } - @Test(expected = NullPointerException.class) public void fromIterableIteratorNull() { Flowable.fromIterable(new Iterable() { @@ -296,16 +188,6 @@ public void fromIterableValueNull() { Flowable.fromIterable(Arrays.asList(1, null)).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromPublisherNull() { - Flowable.fromPublisher(null); - } - - @Test(expected = NullPointerException.class) - public void generateConsumerNull() { - Flowable.generate(null); - } - @Test(expected = NullPointerException.class) public void generateConsumerEmitsNull() { Flowable.generate(new Consumer>() { @@ -378,66 +260,6 @@ public Object apply(Object s, Emitter o) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void generateConsumerDisposeNull() { - BiConsumer> generator = new BiConsumer>() { - @Override - public void accept(Integer s, Emitter o) { - o.onNext(1); - } - }; - Flowable.generate(new Supplier() { - @Override - public Integer get() { - return 1; - } - }, generator, null); - } - - @Test(expected = NullPointerException.class) - public void generateFunctionDisposeNull() { - Flowable.generate(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new BiFunction, Object>() { - @Override - public Object apply(Object s, Emitter o) { - o.onNext(1); return s; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void intervalUnitNull() { - Flowable.interval(1, null); - } - - public void intervalSchedulerNull() { - Flowable.interval(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void intervalPeriodUnitNull() { - Flowable.interval(1, 1, null); - } - - @Test(expected = NullPointerException.class) - public void intervalPeriodSchedulerNull() { - Flowable.interval(1, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void intervalRangeUnitNull() { - Flowable.intervalRange(1, 1, 1, 1, null); - } - - @Test(expected = NullPointerException.class) - public void intervalRangeSchedulerNull() { - Flowable.intervalRange(1, 1, 1, 1, TimeUnit.SECONDS, null); - } - @Test public void justNull() throws Exception { @SuppressWarnings("rawtypes") @@ -465,11 +287,6 @@ public void justNull() throws Exception { } } - @Test(expected = NullPointerException.class) - public void mergeIterableNull() { - Flowable.merge((Iterable>)null, 128, 128); - } - @Test(expected = NullPointerException.class) public void mergeIterableIteratorNull() { Flowable.merge(new Iterable>() { @@ -480,28 +297,16 @@ public Iterator> iterator() { }, 128, 128).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeIterableOneIsNull() { Flowable.merge(Arrays.asList(just1, null), 128, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void mergeArrayNull() { - Flowable.mergeArray(128, 128, (Publisher[])null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeArrayOneIsNull() { Flowable.mergeArray(128, 128, just1, null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void mergeDelayErrorIterableNull() { - Flowable.mergeDelayError((Iterable>)null, 128, 128); - } - @Test(expected = NullPointerException.class) public void mergeDelayErrorIterableIteratorNull() { Flowable.mergeDelayError(new Iterable>() { @@ -512,73 +317,16 @@ public Iterator> iterator() { }, 128, 128).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeDelayErrorIterableOneIsNull() { Flowable.mergeDelayError(Arrays.asList(just1, null), 128, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void mergeDelayErrorArrayNull() { - Flowable.mergeArrayDelayError(128, 128, (Publisher[])null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeDelayErrorArrayOneIsNull() { Flowable.mergeArrayDelayError(128, 128, just1, null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void sequenceEqualFirstNull() { - Flowable.sequenceEqual(null, just1); - } - - @Test(expected = NullPointerException.class) - public void sequenceEqualSecondNull() { - Flowable.sequenceEqual(just1, null); - } - - @Test(expected = NullPointerException.class) - public void sequenceEqualComparatorNull() { - Flowable.sequenceEqual(just1, just1, null); - } - - @Test(expected = NullPointerException.class) - public void switchOnNextNull() { - Flowable.switchOnNext(null); - } - - @Test(expected = NullPointerException.class) - public void timerUnitNull() { - Flowable.timer(1, null); - } - - @Test(expected = NullPointerException.class) - public void timerSchedulerNull() { - Flowable.timer(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void usingResourceSupplierNull() { - Flowable.using(null, new Function>() { - @Override - public Publisher apply(Object d) { - return just1; - } - }, Functions.emptyConsumer()); - } - - @Test(expected = NullPointerException.class) - public void usingFlowableSupplierNull() { - Flowable.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null, Functions.emptyConsumer()); - } - @Test(expected = NullPointerException.class) public void usingFlowableSupplierReturnsNull() { Flowable.using(new Supplier() { @@ -594,31 +342,6 @@ public Publisher apply(Object d) { }, Functions.emptyConsumer()).blockingLast(); } - @Test(expected = NullPointerException.class) - public void usingDisposeNull() { - Flowable.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new Function>() { - @Override - public Publisher apply(Object d) { - return just1; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void zipIterableNull() { - Flowable.zip((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void zipIterableIteratorNull() { Flowable.zip(new Iterable>() { @@ -634,13 +357,6 @@ public Object apply(Object[] v) { }).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void zipIterableFunctionNull() { - Flowable.zip(Arrays.asList(just1, just1), null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableFunctionReturnsNull() { Flowable.zip(Arrays.asList(just1, just1), new Function() { @@ -676,13 +392,6 @@ public Object apply(Object[] a) { }, true, 128).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void zipIterable2FunctionNull() { - Flowable.zip(Arrays.asList(just1, just1), null, true, 128); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterable2FunctionReturnsNull() { Flowable.zip(Arrays.asList(just1, just1), new Function() { @@ -697,26 +406,6 @@ public Object apply(Object[] a) { // Instance methods //************************************************************* - @Test(expected = NullPointerException.class) - public void allPredicateNull() { - just1.all(null); - } - - @Test(expected = NullPointerException.class) - public void ambWithNull() { - just1.ambWith(null); - } - - @Test(expected = NullPointerException.class) - public void anyPredicateNull() { - just1.any(null); - } - - @Test(expected = NullPointerException.class) - public void bufferSupplierNull() { - just1.buffer(1, 1, (Supplier>)null); - } - @Test(expected = NullPointerException.class) public void bufferSupplierReturnsNull() { just1.buffer(1, 1, new Supplier>() { @@ -727,21 +416,6 @@ public Collection get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void bufferTimedUnitNull() { - just1.buffer(1L, 1L, null); - } - - @Test(expected = NullPointerException.class) - public void bufferTimedSchedulerNull() { - just1.buffer(1L, 1L, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void bufferTimedSupplierNull() { - just1.buffer(1L, 1L, TimeUnit.SECONDS, Schedulers.single(), null); - } - @Test(expected = NullPointerException.class) public void bufferTimedSupplierReturnsNull() { just1.buffer(1L, 1L, TimeUnit.SECONDS, Schedulers.single(), new Supplier>() { @@ -752,21 +426,6 @@ public Collection get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void bufferOpenCloseOpenNull() { - just1.buffer(null, new Function>() { - @Override - public Publisher apply(Object o) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void bufferOpenCloseCloseNull() { - just1.buffer(just1, (Function>)null); - } - @Test(expected = NullPointerException.class) public void bufferOpenCloseCloseReturnsNull() { just1.buffer(just1, new Function>() { @@ -777,16 +436,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void bufferBoundaryNull() { - just1.buffer((Publisher)null); - } - - @Test(expected = NullPointerException.class) - public void bufferBoundarySupplierNull() { - just1.buffer(just1, (Supplier>)null); - } - @Test(expected = NullPointerException.class) public void bufferBoundarySupplierReturnsNull() { just1.buffer(just1, new Supplier>() { @@ -797,19 +446,6 @@ public Collection get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void castNull() { - just1.cast(null); - } - - @Test(expected = NullPointerException.class) - public void collectInitialSupplierNull() { - just1.collect((Supplier)null, new BiConsumer() { - @Override - public void accept(Integer a, Integer b) { } - }); - } - @Test(expected = NullPointerException.class) public void collectInitialSupplierReturnsNull() { just1.collect(new Supplier() { @@ -823,39 +459,6 @@ public void accept(Object a, Integer b) { } }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void collectInitialCollectorNull() { - just1.collect(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void collectIntoInitialNull() { - just1.collectInto(null, new BiConsumer() { - @Override - public void accept(Object a, Integer b) { } - }); - } - - @Test(expected = NullPointerException.class) - public void collectIntoCollectorNull() { - just1.collectInto(1, null); - } - - @Test(expected = NullPointerException.class) - public void composeNull() { - just1.compose(null); - } - - @Test(expected = NullPointerException.class) - public void concatMapNull() { - just1.concatMap(null); - } - @Test(expected = NullPointerException.class) public void concatMapReturnsNull() { just1.concatMap(new Function>() { @@ -866,11 +469,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void concatMapIterableNull() { - just1.concatMapIterable(null); - } - @Test(expected = NullPointerException.class) public void concatMapIterableReturnNull() { just1.concatMapIterable(new Function>() { @@ -896,21 +494,6 @@ public Iterator iterator() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void concatWithNull() { - just1.concatWith((Publisher)null); - } - - @Test(expected = NullPointerException.class) - public void containsNull() { - just1.contains(null); - } - - @Test(expected = NullPointerException.class) - public void debounceFunctionNull() { - just1.debounce(null); - } - @Test(expected = NullPointerException.class) public void debounceFunctionReturnsNull() { just1.debounce(new Function>() { @@ -921,26 +504,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void debounceTimedUnitNull() { - just1.debounce(1, null); - } - - @Test(expected = NullPointerException.class) - public void debounceTimedSchedulerNull() { - just1.debounce(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void defaultIfEmptyNull() { - just1.defaultIfEmpty(null); - } - - @Test(expected = NullPointerException.class) - public void delayWithFunctionNull() { - just1.delay(null); - } - @Test(expected = NullPointerException.class) public void delayWithFunctionReturnsNull() { just1.delay(new Function>() { @@ -951,61 +514,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void delayTimedUnitNull() { - just1.delay(1, null); - } - - @Test(expected = NullPointerException.class) - public void delayTimedSchedulerNull() { - just1.delay(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionTimedUnitNull() { - just1.delaySubscription(1, null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionTimedSchedulerNull() { - just1.delaySubscription(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionSupplierNull() { - just1.delaySubscription((Publisher)null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionFunctionNull() { - just1.delaySubscription((Publisher)null); - } - - @Test(expected = NullPointerException.class) - public void delayBothInitialSupplierNull() { - just1.delay(null, new Function>() { - @Override - public Publisher apply(Integer v) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void delayBothInitialSupplierReturnsNull() { - just1.delay(null, new Function>() { - @Override - public Publisher apply(Integer v) { - return just1; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void delayBothItemSupplierNull() { - just1.delay(just1, null); - } - @Test(expected = NullPointerException.class) public void delayBothItemSupplierReturnsNull() { just1.delay(just1, new Function>() { @@ -1016,21 +524,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void distinctFunctionNull() { - just1.distinct(null); - } - - @Test(expected = NullPointerException.class) - public void distinctSupplierNull() { - just1.distinct(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void distinctSupplierReturnsNull() { just1.distinct(new Function() { @@ -1056,16 +549,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void distinctUntilChangedFunctionNull() { - just1.distinctUntilChanged((Function)null); - } - - @Test(expected = NullPointerException.class) - public void distinctUntilChangedBiPredicateNull() { - just1.distinctUntilChanged((BiPredicate)null); - } - @Test public void distinctUntilChangedFunctionReturnsNull() { Flowable.range(1, 2).distinctUntilChanged(new Function() { @@ -1076,109 +559,6 @@ public Object apply(Integer v) { }).test().assertResult(1); } - @Test(expected = NullPointerException.class) - public void doOnCancelNull() { - just1.doOnCancel(null); - } - - @Test(expected = NullPointerException.class) - public void doOnCompleteNull() { - just1.doOnComplete(null); - } - - @Test(expected = NullPointerException.class) - public void doOnEachSupplierNull() { - just1.doOnEach((Consumer>)null); - } - - @Test(expected = NullPointerException.class) - public void doOnEachSubscriberNull() { - just1.doOnEach((Subscriber)null); - } - - @Test(expected = NullPointerException.class) - public void doOnErrorNull() { - just1.doOnError(null); - } - - @Test(expected = NullPointerException.class) - public void doOnLifecycleOnSubscribeNull() { - just1.doOnLifecycle(null, new LongConsumer() { - @Override - public void accept(long v) { } - }, new Action() { - @Override - public void run() { } - }); - } - - @Test(expected = NullPointerException.class) - public void doOnLifecycleOnRequestNull() { - just1.doOnLifecycle(new Consumer() { - @Override - public void accept(Subscription s) { } - }, null, new Action() { - @Override - public void run() { } - }); - } - - @Test(expected = NullPointerException.class) - public void doOnLifecycleOnCancelNull() { - just1.doOnLifecycle(new Consumer() { - @Override - public void accept(Subscription s) { } - }, new LongConsumer() { - @Override - public void accept(long v) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void doOnNextNull() { - just1.doOnNext(null); - } - - @Test(expected = NullPointerException.class) - public void doOnRequestNull() { - just1.doOnRequest(null); - } - - @Test(expected = NullPointerException.class) - public void doOnSubscribeNull() { - just1.doOnSubscribe(null); - } - - @Test(expected = NullPointerException.class) - public void doOnTerminatedNull() { - just1.doOnTerminate(null); - } - - @Test(expected = NullPointerException.class) - public void elementAtNull() { - just1.elementAt(1, null); - } - - @Test(expected = NullPointerException.class) - public void filterNull() { - just1.filter(null); - } - - @Test(expected = NullPointerException.class) - public void doAfterTerminateNull() { - just1.doAfterTerminate(null); - } - - @Test(expected = NullPointerException.class) - public void firstNull() { - just1.first(null); - } - - @Test(expected = NullPointerException.class) - public void flatMapNull() { - just1.flatMap(null); - } - @Test(expected = NullPointerException.class) public void flatMapFunctionReturnsNull() { just1.flatMap(new Function>() { @@ -1189,21 +569,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapNotificationOnNextNull() { - just1.flatMap(null, new Function>() { - @Override - public Publisher apply(Throwable e) { - return just1; - } - }, new Supplier>() { - @Override - public Publisher get() { - return just1; - } - }); - } - @Test(expected = NullPointerException.class) public void flatMapNotificationOnNextReturnsNull() { just1.flatMap(new Function>() { @@ -1224,36 +589,6 @@ public Publisher get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapNotificationOnErrorNull() { - just1.flatMap(new Function>() { - @Override - public Publisher apply(Integer v) { - return just1; - } - }, null, new Supplier>() { - @Override - public Publisher get() { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void flatMapNotificationOnCompleteNull() { - just1.flatMap(new Function>() { - @Override - public Publisher apply(Integer v) { - return just1; - } - }, new Function>() { - @Override - public Publisher apply(Throwable e) { - return just1; - } - }, null); - } - @Test(expected = NullPointerException.class) public void flatMapNotificationOnCompleteReturnsNull() { just1.flatMap(new Function>() { @@ -1274,16 +609,6 @@ public Publisher get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapCombinerMapperNull() { - just1.flatMap(null, new BiFunction() { - @Override - public Object apply(Integer a, Object b) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void flatMapCombinerMapperReturnsNull() { just1.flatMap(new Function>() { @@ -1299,16 +624,6 @@ public Object apply(Integer a, Object b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapCombinerCombinerNull() { - just1.flatMap(new Function>() { - @Override - public Publisher apply(Integer v) { - return just1; - } - }, null); - } - @Test(expected = NullPointerException.class) public void flatMapCombinerCombinerReturnsNull() { just1.flatMap(new Function>() { @@ -1324,11 +639,6 @@ public Object apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapIterableMapperNull() { - just1.flatMapIterable(null); - } - @Test(expected = NullPointerException.class) public void flatMapIterableMapperReturnsNull() { just1.flatMapIterable(new Function>() { @@ -1364,16 +674,6 @@ public Iterable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapIterableCombinerNull() { - just1.flatMapIterable(new Function>() { - @Override - public Iterable apply(Integer v) { - return Arrays.asList(1); - } - }, null); - } - @Test(expected = NullPointerException.class) public void flatMapIterableCombinerReturnsNull() { just1.flatMapIterable(new Function>() { @@ -1389,44 +689,6 @@ public Object apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void forEachNull() { - just1.forEach(null); - } - - @Test(expected = NullPointerException.class) - public void forEachWhileNull() { - just1.forEachWhile(null); - } - - @Test(expected = NullPointerException.class) - public void forEachWhileOnErrorNull() { - just1.forEachWhile(new Predicate() { - @Override - public boolean test(Integer v) { - return true; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void forEachWhileOnCompleteNull() { - just1.forEachWhile(new Predicate() { - @Override - public boolean test(Integer v) { - return true; - } - }, new Consumer() { - @Override - public void accept(Throwable e) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void groupByNull() { - just1.groupBy(null); - } - public void groupByKeyNull() { just1.groupBy(new Function() { @Override @@ -1436,16 +698,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void groupByValueNull() { - just1.groupBy(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void groupByValueReturnsNull() { just1.groupBy(new Function() { @@ -1461,16 +713,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void lastNull() { - just1.last(null); - } - - @Test(expected = NullPointerException.class) - public void liftNull() { - just1.lift(null); - } - @Test(expected = NullPointerException.class) public void liftReturnsNull() { just1.lift(new FlowableOperator() { @@ -1481,11 +723,6 @@ public Subscriber apply(Subscriber s) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void mapNull() { - just1.map(null); - } - @Test(expected = NullPointerException.class) public void mapReturnsNull() { just1.map(new Function() { @@ -1496,36 +733,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void mergeWithNull() { - just1.mergeWith((Publisher)null); - } - - @Test(expected = NullPointerException.class) - public void observeOnNull() { - just1.observeOn(null); - } - - @Test(expected = NullPointerException.class) - public void ofTypeNull() { - just1.ofType(null); - } - - @Test(expected = NullPointerException.class) - public void onBackpressureBufferOverflowNull() { - just1.onBackpressureBuffer(10, null); - } - - @Test(expected = NullPointerException.class) - public void onBackpressureDropActionNull() { - just1.onBackpressureDrop(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorResumeNextNull() { - just1.onErrorResumeNext(null); - } - @Test public void onErrorResumeNextFunctionReturnsNull() { try { @@ -1544,21 +751,6 @@ public Publisher apply(Throwable e) { } } - @Test(expected = NullPointerException.class) - public void onErrorResumeWithNull() { - just1.onErrorResumeWith(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorReturnFunctionNull() { - just1.onErrorReturn(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorReturnValueNull() { - just1.onErrorReturnItem(null); - } - @Test public void onErrorReturnFunctionReturnsNull() { try { @@ -1577,11 +769,6 @@ public Object apply(Throwable e) { } } - @Test(expected = NullPointerException.class) - public void publishFunctionNull() { - just1.publish(null); - } - @Test(expected = NullPointerException.class) public void publishFunctionReturnsNull() { just1.publish(new Function, Publisher>() { @@ -1592,11 +779,6 @@ public Publisher apply(Flowable v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void reduceFunctionNull() { - just1.reduce(null); - } - @Test(expected = NullPointerException.class) public void reduceFunctionReturnsNull() { Flowable.just(1, 1).reduce(new BiFunction() { @@ -1607,21 +789,6 @@ public Integer apply(Integer a, Integer b) { }).toFlowable().blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void reduceSeedNull() { - just1.reduce(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void reduceSeedFunctionNull() { - just1.reduce(1, null); - } - @Test(expected = NullPointerException.class) public void reduceSeedFunctionReturnsNull() { just1.reduce(1, new BiFunction() { @@ -1657,16 +824,6 @@ public Object apply(Object a, Integer b) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void repeatUntilNull() { - just1.repeatUntil(null); - } - - @Test(expected = NullPointerException.class) - public void repeatWhenNull() { - just1.repeatWhen(null); - } - @Test(expected = NullPointerException.class) public void repeatWhenFunctionReturnsNull() { just1.repeatWhen(new Function, Publisher>() { @@ -1692,454 +849,124 @@ public Publisher apply(Flowable f) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void replayBoundedSelectorNull() { - just1.replay((Function, Flowable>)null, 1, 1, TimeUnit.SECONDS); - } - @Test(expected = NullPointerException.class) public void replayBoundedSelectorReturnsNull() { - just1.replay(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) { - return null; - } - }, 1, 1, TimeUnit.SECONDS).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void replayBoundedUnitNull() { - just1.replay(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) { - return v; - } - }, 1, 1, null).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void replayBoundedSchedulerNull() { - just1.replay(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) { - return v; - } - }, 1, 1, TimeUnit.SECONDS, null).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedSelectorNull() { - just1.replay(null, 1, TimeUnit.SECONDS, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedSelectorReturnsNull() { - just1.replay(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) { - return null; - } - }, 1, TimeUnit.SECONDS, Schedulers.single()).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void replaySelectorTimeBoundedUnitNull() { - just1.replay(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) { - return v; - } - }, 1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replaySelectorTimeBoundedSchedulerNull() { - just1.replay(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) { - return v; - } - }, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void replayTimeSizeBoundedUnitNull() { - just1.replay(1, 1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replayTimeSizeBoundedSchedulerNull() { - just1.replay(1, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedUnitNull() { - just1.replay(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedSchedulerNull() { - just1.replay(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void retryFunctionNull() { - just1.retry((BiPredicate)null); - } - - @Test(expected = NullPointerException.class) - public void retryCountFunctionNull() { - just1.retry(1, null); - } - - @Test(expected = NullPointerException.class) - public void retryPredicateNull() { - just1.retry((Predicate)null); - } - - @Test(expected = NullPointerException.class) - public void retryWhenFunctionNull() { - just1.retryWhen(null); - } - - @Test(expected = NullPointerException.class) - public void retryWhenFunctionReturnsNull() { - Flowable.error(new TestException()).retryWhen(new Function, Publisher>() { - @Override - public Publisher apply(Flowable f) { - return null; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void retryUntil() { - just1.retryUntil(null); - } - - @Test(expected = NullPointerException.class) - public void safeSubscribeNull() { - just1.safeSubscribe(null); - } - - @Test(expected = NullPointerException.class) - public void sampleUnitNull() { - just1.sample(1, null); - } - - @Test(expected = NullPointerException.class) - public void sampleSchedulerNull() { - just1.sample(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void samplePublisherNull() { - just1.sample(null); - } - - @Test(expected = NullPointerException.class) - public void scanFunctionNull() { - just1.scan(null); - } - - @Test(expected = NullPointerException.class) - public void scanFunctionReturnsNull() { - Flowable.just(1, 1).scan(new BiFunction() { - @Override - public Integer apply(Integer a, Integer b) { - return null; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void scanSeedNull() { - just1.scan(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void scanSeedFunctionNull() { - just1.scan(1, null); - } - - @Test(expected = NullPointerException.class) - public void scanSeedFunctionReturnsNull() { - just1.scan(1, new BiFunction() { - @Override - public Integer apply(Integer a, Integer b) { - return null; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void scanSeedSupplierNull() { - just1.scanWith(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void scanSeedSupplierReturnsNull() { - just1.scanWith(new Supplier() { - @Override - public Object get() { - return null; - } - }, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void scanSeedSupplierFunctionNull() { - just1.scanWith(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void scanSeedSupplierFunctionReturnsNull() { - just1.scanWith(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return null; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void singleNull() { - just1.single(null); - } - - @Test(expected = NullPointerException.class) - public void skipTimedUnitNull() { - just1.skip(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void skipTimedSchedulerNull() { - just1.skip(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void skipLastTimedUnitNull() { - just1.skipLast(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void skipLastTimedSchedulerNull() { - just1.skipLast(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void skipUntilNull() { - just1.skipUntil(null); - } - - @Test(expected = NullPointerException.class) - public void skipWhileNull() { - just1.skipWhile(null); - } - - @Test(expected = NullPointerException.class) - public void startWithIterableNull() { - just1.startWithIterable((Iterable)null); - } - - @Test(expected = NullPointerException.class) - public void startWithIterableIteratorNull() { - just1.startWithIterable(new Iterable() { - @Override - public Iterator iterator() { - return null; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void startWithIterableOneNull() { - just1.startWithIterable(Arrays.asList(1, null)).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void startWithSingleNull() { - just1.startWithItem((Integer)null); - } - - @Test(expected = NullPointerException.class) - public void startWithPublisherNull() { - just1.startWith((Publisher)null); - } - - @Test(expected = NullPointerException.class) - public void startWithArrayNull() { - just1.startWithArray((Integer[])null); - } - - @Test(expected = NullPointerException.class) - public void startWithArrayOneNull() { - just1.startWithArray(1, null).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnNextNull() { - just1.subscribe((Consumer)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnErrorNull() { - just1.subscribe(Functions.emptyConsumer(), null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnCompleteNull() { - just1.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer(), null); - } - - @Test(expected = NullPointerException.class) - public void subscribeNull() { - just1.subscribe((Subscriber)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeNull2() { - just1.subscribe((FlowableSubscriber)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnNull() { - just1.subscribeOn(null); - } - - @Test(expected = NullPointerException.class) - public void switchIfEmptyNull() { - just1.switchIfEmpty(null); - } - - @Test(expected = NullPointerException.class) - public void switchMapNull() { - just1.switchMap(null); - } - - @Test(expected = NullPointerException.class) - public void switchMapFunctionReturnsNull() { - just1.switchMap(new Function>() { - @Override - public Publisher apply(Integer v) { - return null; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void takeTimedUnitNull() { - just1.take(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void takeTimedSchedulerNull() { - just1.take(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void takeLastTimedUnitNull() { - just1.takeLast(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void takeLastSizeTimedUnitNull() { - just1.takeLast(1, 1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void takeLastTimedSchedulerNull() { - just1.takeLast(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void takeLastSizeTimedSchedulerNull() { - just1.takeLast(1, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void takeUntilPredicateNull() { - just1.takeUntil((Predicate)null); + just1.replay(new Function, Publisher>() { + @Override + public Publisher apply(Flowable v) { + return null; + } + }, 1, 1, TimeUnit.SECONDS).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void takeUntilPublisherNull() { - just1.takeUntil((Publisher)null); + public void replayTimeBoundedSelectorReturnsNull() { + just1.replay(new Function, Publisher>() { + @Override + public Publisher apply(Flowable v) { + return null; + } + }, 1, TimeUnit.SECONDS, Schedulers.single()).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void takeWhileNull() { - just1.takeWhile(null); + public void retryWhenFunctionReturnsNull() { + Flowable.error(new TestException()).retryWhen(new Function, Publisher>() { + @Override + public Publisher apply(Flowable f) { + return null; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void throttleFirstUnitNull() { - just1.throttleFirst(1, null, Schedulers.single()); + public void scanFunctionReturnsNull() { + Flowable.just(1, 1).scan(new BiFunction() { + @Override + public Integer apply(Integer a, Integer b) { + return null; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void throttleFirstSchedulerNull() { - just1.throttleFirst(1, TimeUnit.SECONDS, null); + public void scanSeedNull() { + just1.scan(null, new BiFunction() { + @Override + public Object apply(Object a, Integer b) { + return 1; + } + }); } @Test(expected = NullPointerException.class) - public void throttleLastUnitNull() { - just1.throttleLast(1, null, Schedulers.single()); + public void scanSeedFunctionReturnsNull() { + just1.scan(1, new BiFunction() { + @Override + public Integer apply(Integer a, Integer b) { + return null; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void throttleLastSchedulerNull() { - just1.throttleLast(1, TimeUnit.SECONDS, null); + public void scanSeedSupplierReturnsNull() { + just1.scanWith(new Supplier() { + @Override + public Object get() { + return null; + } + }, new BiFunction() { + @Override + public Object apply(Object a, Integer b) { + return 1; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void throttleWithTimeoutUnitNull() { - just1.throttleWithTimeout(1, null, Schedulers.single()); + public void scanSeedSupplierFunctionReturnsNull() { + just1.scanWith(new Supplier() { + @Override + public Object get() { + return 1; + } + }, new BiFunction() { + @Override + public Object apply(Object a, Integer b) { + return null; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void throttleWithTimeoutSchedulerNull() { - just1.throttleWithTimeout(1, TimeUnit.SECONDS, null); + public void startWithIterableIteratorNull() { + just1.startWithIterable(new Iterable() { + @Override + public Iterator iterator() { + return null; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void timeIntervalUnitNull() { - just1.timeInterval(null, Schedulers.single()); + public void startWithIterableOneNull() { + just1.startWithIterable(Arrays.asList(1, null)).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void timeIntervalSchedulerNull() { - just1.timeInterval(TimeUnit.SECONDS, null); + public void startWithArrayOneNull() { + just1.startWithArray(1, null).blockingSubscribe(); } @Test(expected = NullPointerException.class) - public void timeoutSelectorNull() { - just1.timeout(null); + public void switchMapFunctionReturnsNull() { + just1.switchMap(new Function>() { + @Override + public Publisher apply(Integer v) { + return null; + } + }).blockingSubscribe(); } @Test(expected = NullPointerException.class) @@ -2162,36 +989,6 @@ public Publisher apply(Integer v) { }, null); } - @Test(expected = NullPointerException.class) - public void timeoutUnitNull() { - just1.timeout(1, null, Schedulers.single(), just1); - } - - @Test(expected = NullPointerException.class) - public void timeouOtherNull() { - just1.timeout(1, TimeUnit.SECONDS, Schedulers.single(), null); - } - - @Test(expected = NullPointerException.class) - public void timeouSchedulerNull() { - just1.timeout(1, TimeUnit.SECONDS, null, just1); - } - - @Test(expected = NullPointerException.class) - public void timeoutFirstNull() { - just1.timeout((Publisher)null, new Function>() { - @Override - public Publisher apply(Integer v) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void timeoutFirstItemNull() { - just1.timeout(just1, null); - } - @Test(expected = NullPointerException.class) public void timeoutFirstItemReturnsNull() { just1.timeout(Flowable.never(), new Function>() { @@ -2212,16 +1009,6 @@ public void timestampSchedulerNull() { just1.timestamp(TimeUnit.SECONDS, null); } - @Test(expected = NullPointerException.class) - public void toNull() { - just1.to(null); - } - - @Test(expected = NullPointerException.class) - public void toListNull() { - just1.toList(null); - } - @Test(expected = NullPointerException.class) public void toListSupplierReturnsNull() { just1.toList(new Supplier>() { @@ -2242,26 +1029,6 @@ public Collection get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toSortedListNull() { - just1.toSortedList(null); - } - - @Test(expected = NullPointerException.class) - public void toMapKeyNullAllowed() { - just1.toMap(null); - } - - @Test(expected = NullPointerException.class) - public void toMapValueNull() { - just1.toMap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test public void toMapValueSelectorReturnsNull() { just1.toMap(new Function() { @@ -2277,21 +1044,6 @@ public Object apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMapMapSupplierNull() { - just1.toMap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void toMapMapSupplierReturnsNull() { just1.toMap(new Function() { @@ -2312,21 +1064,6 @@ public Map get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMultimapKeyNull() { - just1.toMultimap(null); - } - - @Test(expected = NullPointerException.class) - public void toMultimapValueNull() { - just1.toMultimap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test public void toMultiMapValueSelectorReturnsNullAllowed() { just1.toMap(new Function() { @@ -2342,21 +1079,6 @@ public Object apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMultimapMapMapSupplierNull() { - just1.toMultimap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void toMultimapMapSupplierReturnsNull() { just1.toMultimap(new Function() { @@ -2377,26 +1099,6 @@ public Map> get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMultimapMapMapCollectionSupplierNull() { - just1.toMultimap(new Function() { - @Override - public Integer apply(Integer v) { - return v; - } - }, new Function() { - @Override - public Integer apply(Integer v) { - return v; - } - }, new Supplier>>() { - @Override - public Map> get() { - return new HashMap>(); - } - }, null); - } - @Test(expected = NullPointerException.class) public void toMultimapMapCollectionSupplierReturnsNull() { just1.toMultimap(new Function() { @@ -2412,7 +1114,7 @@ public Integer apply(Integer v) { }, new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }, new Function>() { @Override @@ -2422,36 +1124,6 @@ public Collection apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void unsubscribeOnNull() { - just1.unsubscribeOn(null); - } - - @Test(expected = NullPointerException.class) - public void windowTimedUnitNull() { - just1.window(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void windowSizeTimedUnitNull() { - just1.window(1, null, Schedulers.single(), 1); - } - - @Test(expected = NullPointerException.class) - public void windowTimedSchedulerNull() { - just1.window(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void windowSizeTimedSchedulerNull() { - just1.window(1, TimeUnit.SECONDS, null, 1); - } - - @Test(expected = NullPointerException.class) - public void windowBoundaryNull() { - just1.window((Publisher)null); - } - @Test(expected = NullPointerException.class) public void windowOpenCloseOpenNull() { just1.window(null, new Function>() { @@ -2462,11 +1134,6 @@ public Publisher apply(Object v) { }); } - @Test(expected = NullPointerException.class) - public void windowOpenCloseCloseNull() { - just1.window(just1, null); - } - @Test(expected = NullPointerException.class) public void windowOpenCloseCloseReturnsNull() { Flowable.never().window(just1, new Function>() { @@ -2487,11 +1154,6 @@ public Object apply(Integer a, Object b) { }); } - @Test(expected = NullPointerException.class) - public void withLatestFromCombinerNull() { - just1.withLatestFrom(just1, null); - } - @Test(expected = NullPointerException.class) public void withLatestFromCombinerReturnsNull() { just1.withLatestFrom(just1, new BiFunction() { @@ -2512,11 +1174,6 @@ public Object apply(Integer a, Integer b) { }); } - @Test(expected = NullPointerException.class) - public void zipWithIterableCombinerNull() { - just1.zipWith(Arrays.asList(1), null); - } - @Test(expected = NullPointerException.class) public void zipWithIterableCombinerReturnsNull() { just1.zipWith(Arrays.asList(1), new BiFunction() { @@ -2562,11 +1219,6 @@ public Object apply(Integer a, Integer b) { }); } - @Test(expected = NullPointerException.class) - public void zipWithCombinerNull() { - just1.zipWith(just1, null); - } - @Test(expected = NullPointerException.class) public void zipWithCombinerReturnsNull() { just1.zipWith(just1, new BiFunction() { @@ -2651,35 +1303,6 @@ public void serializedSubjectOnErrorNull() { processor.blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void doOnLifecycleOnDisposeNull() { - just1.doOnLifecycle(new Consumer() { - @Override - public void accept(Subscription s) { } - }, - new LongConsumer() { - @Override - public void accept(long v) throws Exception { } - }, - null); - } - - @Test(expected = NullPointerException.class) - public void zipWithFlowableNull() { - just1.zipWith((Flowable)null, new BiFunction() { - @Override - public Object apply(Integer a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void unsafeSubscribeNull() { - just1.subscribe((FlowableSubscriber)null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestDelayErrorIterableFunctionReturnsNull() { Flowable.combineLatestDelayError(Arrays.asList(just1), new Function() { @@ -2690,27 +1313,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void combineLatestDelayErrorIterableFunctionNull() { - Flowable.combineLatestDelayError(Arrays.asList(just1), null, 128); - } - - @Test(expected = NullPointerException.class) - public void concatFlowableNull() { - Flowable.concat((Flowable>)null); - } - - @Test(expected = NullPointerException.class) - public void combineLatestDelayErrorIterableNull() { - Flowable.combineLatestDelayError((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }, 128); - } - @Test(expected = NullPointerException.class) public void combineLatestDelayErrorIterableIteratorNull() { Flowable.combineLatestDelayError(new Iterable>() { @@ -2726,12 +1328,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void doOnDisposeNull() { - just1.doOnCancel(null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestDelayErrorIterableOneIsNull() { Flowable.combineLatestDelayError(Arrays.asList(Flowable.never(), null), new Function() { @@ -2741,24 +1337,4 @@ public Object apply(Object[] v) { } }, 128).blockingLast(); } - - @Test(expected = NullPointerException.class) - public void takeUntilFlowableNull() { - just1.takeUntil((Flowable)null); - } - - @Test(expected = NullPointerException.class) - public void startWithFlowableNull() { - just1.startWith((Flowable)null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionOtherNull() { - just1.delaySubscription((Flowable)null); - } - - @Test(expected = NullPointerException.class) - public void sampleFlowableNull() { - just1.sample(null); - } } diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableReduceTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableReduceTests.java index 1e6abe83c9..5dd3c28cb2 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableReduceTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableReduceTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableStartWithTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableStartWithTests.java index e4ea973b5b..28549b1a90 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableStartWithTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableStartWithTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public void startWith1() { @Test public void startWithIterable() { - List li = new ArrayList(); + List li = new ArrayList<>(); li.add("alpha"); li.add("beta"); List values = Flowable.just("one", "two").startWithIterable(li).toList().blockingGet(); @@ -47,7 +47,7 @@ public void startWithIterable() { @Test public void startWithObservable() { - List li = new ArrayList(); + List li = new ArrayList<>(); li.add("alpha"); li.add("beta"); List values = Flowable.just("one", "two") diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableSubscriberTest.java index 32c954f42b..b1eb9ce600 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public class FlowableSubscriberTest { */ @Test public void requestFromFinalSubscribeWithRequestValue() { - TestSubscriber s = new TestSubscriber(0L); + TestSubscriber s = new TestSubscriber<>(0L); s.request(10); final AtomicLong r = new AtomicLong(); s.onSubscribe(new Subscription() { @@ -64,7 +64,7 @@ public void cancel() { */ @Test public void requestFromFinalSubscribeWithoutRequestValue() { - TestSubscriber s = new TestSubscriber(); + TestSubscriber s = new TestSubscriber<>(); final AtomicLong r = new AtomicLong(); s.onSubscribe(new Subscription() { @@ -83,8 +83,8 @@ public void cancel() { } @Test - public void requestFromChainedOperator() throws Exception { - TestSubscriber s = new TestSubscriber(10L); + public void requestFromChainedOperator() throws Throwable { + TestSubscriber s = new TestSubscriber<>(10L); FlowableOperator o = new FlowableOperator() { @Override public Subscriber apply(final Subscriber s1) { @@ -135,8 +135,8 @@ public void cancel() { } @Test - public void requestFromDecoupledOperator() throws Exception { - TestSubscriber s = new TestSubscriber(0L); + public void requestFromDecoupledOperator() throws Throwable { + TestSubscriber s = new TestSubscriber<>(0L); FlowableOperator o = new FlowableOperator() { @Override public Subscriber apply(final Subscriber s1) { @@ -188,8 +188,8 @@ public void cancel() { } @Test - public void requestFromDecoupledOperatorThatRequestsN() throws Exception { - TestSubscriber s = new TestSubscriber(10L); + public void requestFromDecoupledOperatorThatRequestsN() throws Throwable { + TestSubscriber s = new TestSubscriber<>(10L); final AtomicLong innerR = new AtomicLong(); FlowableOperator o = new FlowableOperator() { @Override @@ -260,7 +260,7 @@ public void cancel() { @Test public void requestToFlowable() { - TestSubscriber ts = new TestSubscriber(3L); + TestSubscriber ts = new TestSubscriber<>(3L); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @Override @@ -284,7 +284,7 @@ public void cancel() { @Test public void requestThroughMap() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(3); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -309,7 +309,7 @@ public void cancel() { @Test public void requestThroughTakeThatReducesRequest() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(3); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -336,7 +336,7 @@ public void cancel() { @Test public void requestThroughTakeWhereRequestIsSmallerThanTake() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(3); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -463,7 +463,7 @@ public void onNext(Integer t) { @Test public void onStartRequestsAreAdditive() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.just(1, 2, 3, 4, 5) .subscribe(new DefaultSubscriber() { @Override @@ -491,7 +491,7 @@ public void onNext(Integer t) { @Test public void onStartRequestsAreAdditiveAndOverflowBecomesMaxValue() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.just(1, 2, 3, 4, 5).subscribe(new DefaultSubscriber() { @Override public void onStart() { @@ -520,7 +520,7 @@ public void onNext(Integer t) { public void forEachWhile() { PublishProcessor pp = PublishProcessor.create(); - final List list = new ArrayList(); + final List list = new ArrayList<>(); Disposable d = pp.forEachWhile(new Predicate() { @Override @@ -543,7 +543,7 @@ public boolean test(Integer v) throws Exception { @Test public void doubleSubscribe() { - ForEachWhileSubscriber s = new ForEachWhileSubscriber(new Predicate() { + ForEachWhileSubscriber s = new ForEachWhileSubscriber<>(new Predicate() { @Override public boolean test(Integer v) throws Exception { return true; @@ -570,10 +570,10 @@ public boolean test(Integer v) throws Exception { public void suppressAfterCompleteEvents() { List errors = TestHelper.trackPluginErrors(); try { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); - ForEachWhileSubscriber s = new ForEachWhileSubscriber(new Predicate() { + ForEachWhileSubscriber s = new ForEachWhileSubscriber<>(new Predicate() { @Override public boolean test(Integer v) throws Exception { ts.onNext(v); @@ -606,10 +606,10 @@ public void run() throws Exception { @Test public void onNextCrashes() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); - ForEachWhileSubscriber s = new ForEachWhileSubscriber(new Predicate() { + ForEachWhileSubscriber s = new ForEachWhileSubscriber<>(new Predicate() { @Override public boolean test(Integer v) throws Exception { throw new TestException(); @@ -637,7 +637,7 @@ public void run() throws Exception { @Test public void onErrorThrows() { - ForEachWhileSubscriber s = new ForEachWhileSubscriber(new Predicate() { + ForEachWhileSubscriber s = new ForEachWhileSubscriber<>(new Predicate() { @Override public boolean test(Integer v) throws Exception { return true; @@ -672,7 +672,7 @@ public void run() throws Exception { @Test public void onCompleteThrows() { - ForEachWhileSubscriber s = new ForEachWhileSubscriber(new Predicate() { + ForEachWhileSubscriber s = new ForEachWhileSubscriber<>(new Predicate() { @Override public boolean test(Integer v) throws Exception { return true; @@ -703,7 +703,7 @@ public void run() throws Exception { @Test public void subscribeConsumerConsumerWithError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.error(new TestException()).subscribe(new Consumer() { @Override @@ -731,8 +731,8 @@ public void methodTestCancelled() { @Test public void safeSubscriberAlreadySafe() { - TestSubscriber ts = new TestSubscriber(); - Flowable.just(1).safeSubscribe(new SafeSubscriber(ts)); + TestSubscriber ts = new TestSubscriber<>(); + Flowable.just(1).safeSubscribe(new SafeSubscriber<>(ts)); ts.assertResult(1); } @@ -748,7 +748,7 @@ public void methodTestNoCancel() { @Test public void subscribeConsumerConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.just(1).subscribe(new Consumer() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableTests.java index e61e106f1a..1fa80701be 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -71,7 +71,7 @@ public void fromArray() { @Test public void fromIterable() { - ArrayList items = new ArrayList(); + ArrayList items = new ArrayList<>(); items.add("one"); items.add("two"); items.add("three"); @@ -350,7 +350,7 @@ public void materializeDematerializeChaining() { public void customObservableWithErrorInObserverAsynchronous() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); // FIXME custom built??? Flowable.just("1", "2", "three", "4") @@ -398,7 +398,7 @@ public void onNext(String v) { @Test public void customObservableWithErrorInObserverSynchronous() { final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); // FIXME custom built??? Flowable.just("1", "2", "three", "4") @@ -440,7 +440,7 @@ public void onNext(String v) { @Test public void customObservableWithErrorInObservableSynchronous() { final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); // FIXME custom built??? Flowable.just("1", "2").concatWith(Flowable.error(new Supplier() { @Override @@ -660,7 +660,7 @@ public void accept(String v) { @Test public void takeWithErrorInObserver() { final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); Flowable.just("1", "2", "three", "4").take(3) .safeSubscribe(new DefaultSubscriber() { @@ -710,9 +710,9 @@ public void ofType() { @Test public void ofTypeWithPolymorphism() { - ArrayList l1 = new ArrayList(); + ArrayList l1 = new ArrayList<>(); l1.add(1); - LinkedList l2 = new LinkedList(); + LinkedList l2 = new LinkedList<>(); l2.add(2); @SuppressWarnings("rawtypes") @@ -898,21 +898,21 @@ public void rangeWithScheduler() { @Test public void mergeWith() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).mergeWith(Flowable.just(2)).subscribe(ts); ts.assertValues(1, 2); } @Test public void concatWith() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).concatWith(Flowable.just(2)).subscribe(ts); ts.assertValues(1, 2); } @Test public void ambWith() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).ambWith(Flowable.just(2)).subscribe(ts); ts.assertValue(1); } @@ -944,7 +944,7 @@ public void accept(List booleans) { @Test public void compose() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(1, 2, 3).compose(new FlowableTransformer() { @Override public Publisher apply(Flowable t1) { @@ -999,16 +999,9 @@ public void emptyIsEmpty() { verify(w, never()).onError(any(Throwable.class)); } - @Test(expected = NullPointerException.class) - public void forEachWithNull() { - Flowable.error(new Exception("boo")) - // - .forEach(null); - } - @Test public void extend() { - final TestSubscriber subscriber = new TestSubscriber(); + final TestSubscriber subscriber = new TestSubscriber<>(); final Object value = new Object(); Object returned = Flowable.just(value).to(new FlowableConverter() { @Override @@ -1025,7 +1018,7 @@ public Object apply(Flowable onSubscribe) { @Test public void asExtend() { - final TestSubscriber subscriber = new TestSubscriber(); + final TestSubscriber subscriber = new TestSubscriber<>(); final Object value = new Object(); Object returned = Flowable.just(value).to(new FlowableConverter() { @Override @@ -1074,7 +1067,6 @@ public void toObservableError() { @Test public void zipIterableObject() { - @SuppressWarnings("unchecked") final List> flowables = Arrays.asList(Flowable.just(1, 2, 3), Flowable.just(1, 2, 3)); Flowable.zip(flowables, new Function() { @Override @@ -1090,7 +1082,6 @@ public Object apply(Object[] o) throws Exception { @Test public void combineLatestObject() { - @SuppressWarnings("unchecked") final List> flowables = Arrays.asList(Flowable.just(1, 2, 3), Flowable.just(1, 2, 3)); Flowable.combineLatest(flowables, new Function() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleLastTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleLastTests.java index c6db747cf1..c5c858c430 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleLastTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleLastTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,10 +13,14 @@ package io.reactivex.rxjava3.flowable; -import static org.mockito.Mockito.inOrder; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import java.util.concurrent.TimeUnit; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Action; import org.junit.Test; import org.mockito.InOrder; import org.reactivestreams.Subscriber; @@ -28,6 +32,72 @@ public class FlowableThrottleLastTests extends RxJavaTest { + @Test + public void throttleWithDroppedCallbackException() throws Throwable { + Subscriber subscriber = TestHelper.mockSubscriber(); + Action whenDisposed = mock(Action.class); + + TestScheduler s = new TestScheduler(); + PublishProcessor o = PublishProcessor.create(); + o.doOnCancel(whenDisposed) + .throttleLast(500, TimeUnit.MILLISECONDS, s, e-> { + if (e == 1) { + throw new TestException("forced"); + } + }) + .subscribe(subscriber); + + // send events with simulated time increments + s.advanceTimeTo(0, TimeUnit.MILLISECONDS); + o.onNext(1); // skip + o.onNext(2); // deliver + s.advanceTimeTo(501, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(subscriber); + inOrder.verify(subscriber).onError(any(TestException.class)); + inOrder.verifyNoMoreInteractions(); + verify(whenDisposed).run(); + } + + @Test + public void throttleWithDroppedCallback() { + Subscriber subscriber = TestHelper.mockSubscriber(); + Observer dropCallbackObserver = TestHelper.mockObserver(); + + TestScheduler s = new TestScheduler(); + PublishProcessor o = PublishProcessor.create(); + o.throttleLast(500, TimeUnit.MILLISECONDS, s, dropCallbackObserver::onNext).subscribe(subscriber); + + // send events with simulated time increments + s.advanceTimeTo(0, TimeUnit.MILLISECONDS); + o.onNext(1); // skip + o.onNext(2); // deliver + s.advanceTimeTo(501, TimeUnit.MILLISECONDS); + o.onNext(3); // skip + s.advanceTimeTo(600, TimeUnit.MILLISECONDS); + o.onNext(4); // skip + s.advanceTimeTo(700, TimeUnit.MILLISECONDS); + o.onNext(5); // skip + o.onNext(6); // deliver + s.advanceTimeTo(1001, TimeUnit.MILLISECONDS); + o.onNext(7); // deliver + s.advanceTimeTo(1501, TimeUnit.MILLISECONDS); + o.onComplete(); + + InOrder inOrder = inOrder(subscriber); + InOrder dropCallbackOrder = inOrder(dropCallbackObserver); + dropCallbackOrder.verify(dropCallbackObserver).onNext(1); + inOrder.verify(subscriber).onNext(2); + dropCallbackOrder.verify(dropCallbackObserver).onNext(3); + dropCallbackOrder.verify(dropCallbackObserver).onNext(4); + dropCallbackOrder.verify(dropCallbackObserver).onNext(5); + inOrder.verify(subscriber).onNext(6); + inOrder.verify(subscriber).onNext(7); + inOrder.verify(subscriber).onComplete(); + inOrder.verifyNoMoreInteractions(); + dropCallbackOrder.verifyNoMoreInteractions(); + } + @Test public void throttle() { Subscriber subscriber = TestHelper.mockSubscriber(); diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleWithTimeoutTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleWithTimeoutTests.java index f84d27a00d..be94bf9d38 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleWithTimeoutTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableThrottleWithTimeoutTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableWindowTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableWindowTests.java index d43b96f17f..2ba648282f 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableWindowTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableWindowTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class FlowableWindowTests extends RxJavaTest { @Test public void window() { - final ArrayList> lists = new ArrayList>(); + final ArrayList> lists = new ArrayList<>(); Flowable.concat( Flowable.just(1, 2, 3, 4, 5, 6) diff --git a/src/test/java/io/reactivex/rxjava3/flowable/FlowableZipTests.java b/src/test/java/io/reactivex/rxjava3/flowable/FlowableZipTests.java index e022e02c82..7f77992bfe 100644 --- a/src/test/java/io/reactivex/rxjava3/flowable/FlowableZipTests.java +++ b/src/test/java/io/reactivex/rxjava3/flowable/FlowableZipTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public String apply(Event e) { .flatMap(new Function, Publisher>>() { @Override public Publisher> apply(final GroupedFlowable ge) { - return ge.scan(new HashMap(), new BiFunction, Event, HashMap>() { + return ge.scan(new HashMap<>(), new BiFunction, Event, HashMap>() { @Override public HashMap apply(HashMap accum, Event perInstanceEvent) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/SubscribeWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/SubscribeWithTest.java index 15a7c1190d..a24562a854 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/SubscribeWithTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/SubscribeWithTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,14 +27,14 @@ public class SubscribeWithTest extends RxJavaTest { @Test public void withFlowable() { Flowable.range(1, 10) - .subscribeWith(new TestSubscriber()) + .subscribeWith(new TestSubscriber<>()) .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } @Test public void withObservable() { Observable.range(1, 10) - .subscribeWith(new TestObserver()) + .subscribeWith(new TestObserver<>()) .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposableTest.java index c5edd75477..c0319dfd3a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/disposables/ArrayCompositeDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,14 +27,14 @@ public class ArrayCompositeDisposableTest extends RxJavaTest { public void normal() { ArrayCompositeDisposable acd = new ArrayCompositeDisposable(2); - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); assertTrue(acd.setResource(0, d1)); assertTrue(acd.setResource(1, d2)); - Disposable d3 = Disposables.empty(); - Disposable d4 = Disposables.empty(); + Disposable d3 = Disposable.empty(); + Disposable d4 = Disposable.empty(); acd.replaceResource(0, d3); acd.replaceResource(1, d4); @@ -58,8 +58,8 @@ public void normal() { assertTrue(d1.isDisposed()); assertTrue(d2.isDisposed()); - Disposable d5 = Disposables.empty(); - Disposable d6 = Disposables.empty(); + Disposable d5 = Disposable.empty(); + Disposable d6 = Disposable.empty(); assertFalse(acd.setResource(0, d5)); acd.replaceResource(1, d6); @@ -92,7 +92,7 @@ public void replaceRace() { Runnable r = new Runnable() { @Override public void run() { - acd.replaceResource(0, Disposables.empty()); + acd.replaceResource(0, Disposable.empty()); } }; @@ -108,7 +108,7 @@ public void setRace() { Runnable r = new Runnable() { @Override public void run() { - acd.setResource(0, Disposables.empty()); + acd.setResource(0, Disposable.empty()); } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposableTest.java index 03f54c9218..ab40e7c03c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/disposables/CancellableDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/disposables/DisposableHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/disposables/DisposableHelperTest.java index ea0c4ea89b..a3102ea44f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/disposables/DisposableHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/disposables/DisposableHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -54,7 +54,7 @@ public void validationNull() { @Test public void disposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference d = new AtomicReference(); + final AtomicReference d = new AtomicReference<>(); Runnable r = new Runnable() { @Override @@ -70,12 +70,12 @@ public void run() { @Test public void setReplace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference d = new AtomicReference(); + final AtomicReference d = new AtomicReference<>(); Runnable r = new Runnable() { @Override public void run() { - DisposableHelper.replace(d, Disposables.empty()); + DisposableHelper.replace(d, Disposable.empty()); } }; @@ -86,12 +86,12 @@ public void run() { @Test public void setRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference d = new AtomicReference(); + final AtomicReference d = new AtomicReference<>(); Runnable r = new Runnable() { @Override public void run() { - DisposableHelper.set(d, Disposables.empty()); + DisposableHelper.set(d, Disposable.empty()); } }; @@ -101,7 +101,7 @@ public void run() { @Test public void setReplaceNull() { - final AtomicReference d = new AtomicReference(); + final AtomicReference d = new AtomicReference<>(); DisposableHelper.dispose(d); @@ -111,8 +111,8 @@ public void setReplaceNull() { @Test public void dispose() { - Disposable u = Disposables.empty(); - final AtomicReference d = new AtomicReference(u); + Disposable u = Disposable.empty(); + final AtomicReference d = new AtomicReference<>(u); DisposableHelper.dispose(d); @@ -121,13 +121,13 @@ public void dispose() { @Test public void trySet() { - AtomicReference ref = new AtomicReference(); + AtomicReference ref = new AtomicReference<>(); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); assertTrue(DisposableHelper.trySet(ref, d1)); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); assertFalse(DisposableHelper.trySet(ref, d2)); @@ -137,7 +137,7 @@ public void trySet() { DisposableHelper.dispose(ref); - Disposable d3 = Disposables.empty(); + Disposable d3 = Disposable.empty(); assertFalse(DisposableHelper.trySet(ref, d3)); diff --git a/src/test/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposableTest.java index b66f27036e..1f0d370dcd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/disposables/EmptyDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.TestHelper; public class EmptyDisposableTest extends RxJavaTest { diff --git a/src/test/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposableTest.java index 280cb1c28e..1a0070e22d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/disposables/ListCompositeDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,8 +28,8 @@ public class ListCompositeDisposableTest extends RxJavaTest { @Test public void constructorAndAddVarargs() { - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); ListCompositeDisposable lcd = new ListCompositeDisposable(d1, d2); @@ -40,8 +40,8 @@ public void constructorAndAddVarargs() { assertTrue(d1.isDisposed()); assertTrue(d2.isDisposed()); - d1 = Disposables.empty(); - d2 = Disposables.empty(); + d1 = Disposable.empty(); + d2 = Disposable.empty(); lcd.addAll(d1, d2); @@ -54,8 +54,8 @@ public void constructorAndAddVarargs() { @Test public void constructorIterable() { - Disposable d1 = Disposables.empty(); - Disposable d2 = Disposables.empty(); + Disposable d1 = Disposable.empty(); + Disposable d2 = Disposable.empty(); ListCompositeDisposable lcd = new ListCompositeDisposable(Arrays.asList(d1, d2)); @@ -66,8 +66,8 @@ public void constructorIterable() { assertTrue(d1.isDisposed()); assertTrue(d2.isDisposed()); - d1 = Disposables.empty(); - d2 = Disposables.empty(); + d1 = Disposable.empty(); + d2 = Disposable.empty(); lcd.add(d1); lcd.addAll(d2); @@ -103,11 +103,11 @@ public void afterDispose() { ListCompositeDisposable lcd = new ListCompositeDisposable(); lcd.dispose(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); assertFalse(lcd.add(d)); assertTrue(d.isDisposed()); - d = Disposables.empty(); + d = Disposable.empty(); assertFalse(lcd.addAll(d)); assertTrue(d.isDisposed()); } @@ -152,7 +152,7 @@ public boolean isDisposed() { @Test public void remove() { ListCompositeDisposable lcd = new ListCompositeDisposable(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); lcd.add(d); @@ -201,7 +201,7 @@ public void addRace() { Runnable run = new Runnable() { @Override public void run() { - cd.add(Disposables.empty()); + cd.add(Disposable.empty()); } }; @@ -217,7 +217,7 @@ public void addAllRace() { Runnable run = new Runnable() { @Override public void run() { - cd.addAll(Disposables.empty()); + cd.addAll(Disposable.empty()); } }; @@ -230,7 +230,7 @@ public void removeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ListCompositeDisposable cd = new ListCompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -250,7 +250,7 @@ public void deleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ListCompositeDisposable cd = new ListCompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -270,7 +270,7 @@ public void clearRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ListCompositeDisposable cd = new ListCompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -300,7 +300,7 @@ public void run() { Runnable run2 = new Runnable() { @Override public void run() { - cd.add(Disposables.empty()); + cd.add(Disposable.empty()); } }; @@ -323,7 +323,7 @@ public void run() { Runnable run2 = new Runnable() { @Override public void run() { - cd.addAll(Disposables.empty()); + cd.addAll(Disposable.empty()); } }; @@ -336,7 +336,7 @@ public void removeDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ListCompositeDisposable cd = new ListCompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -363,7 +363,7 @@ public void deleteDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ListCompositeDisposable cd = new ListCompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); @@ -390,7 +390,7 @@ public void clearDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ListCompositeDisposable cd = new ListCompositeDisposable(); - final Disposable d1 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); cd.add(d1); diff --git a/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java b/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java index 98488bcd49..635fcc4472 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/functions/FunctionsTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,12 +39,12 @@ public void utilityClass() { public void hashSetCallableEnum() { // inlined TestHelper.checkEnum due to access restrictions try { - Method m = Functions.HashSetCallable.class.getMethod("values"); + Method m = Functions.HashSetSupplier.class.getMethod("values"); m.setAccessible(true); - Method e = Functions.HashSetCallable.class.getMethod("valueOf", String.class); + Method e = Functions.HashSetSupplier.class.getMethod("valueOf", String.class); e.setAccessible(true); - for (Enum o : (Enum[])m.invoke(null)) { + for (Enum o : (Enum[])m.invoke(null)) { assertSame(o, e.invoke(null, o.name())); } @@ -173,62 +173,6 @@ public Integer apply(Integer t1, Integer t2, Integer t3, Integer t4, Integer t5, }).apply(new Object[20]); } - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void biFunctionFail() throws Exception { - BiFunction biFunction = null; - Functions.toFunction(biFunction); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function3Fail() throws Exception { - Function3 function3 = null; - Functions.toFunction(function3); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function4Fail() throws Exception { - Function4 function4 = null; - Functions.toFunction(function4); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function5Fail() throws Exception { - Function5 function5 = null; - Functions.toFunction(function5); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function6Fail() throws Exception { - Function6 function6 = null; - Functions.toFunction(function6); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function7Fail() throws Exception { - Function7 function7 = null; - Functions.toFunction(function7); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function8Fail() throws Exception { - Function8 function8 = null; - Functions.toFunction(function8); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - @Test(expected = NullPointerException.class) - public void function9Fail() throws Exception { - Function9 function9 = null; - Functions.toFunction(function9); - } - @Test public void identityFunctionToString() { assertEquals("IdentityFunction", Functions.identity().toString()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/functions/ObjectHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/functions/ObjectHelperTest.java index c4ae28f883..fb7e912da4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/functions/ObjectHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/functions/ObjectHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,13 +27,6 @@ public void utilityClass() { TestHelper.checkUtilityClass(ObjectHelper.class); } - @Test - public void hashCodeOf() { - assertEquals(0, ObjectHelper.hashCode(null)); - - assertEquals(((Integer)1).hashCode(), ObjectHelper.hashCode(1)); - } - @Test public void verifyPositiveInt() throws Exception { assertEquals(1, ObjectHelper.verifyPositive(1, "param")); @@ -53,24 +46,4 @@ public void verifyPositiveIntFail() throws Exception { public void verifyPositiveLongFail() throws Exception { assertEquals(-1L, ObjectHelper.verifyPositive(-1L, "param")); } - - @Test - public void compare() { - assertEquals(-1, ObjectHelper.compare(0, 2)); - assertEquals(0, ObjectHelper.compare(0, 0)); - assertEquals(1, ObjectHelper.compare(2, 0)); - } - - @Test - public void compareLong() { - assertEquals(-1, ObjectHelper.compare(0L, 2L)); - assertEquals(0, ObjectHelper.compare(0L, 0L)); - assertEquals(1, ObjectHelper.compare(2L, 0L)); - } - - @SuppressWarnings("deprecation") - @Test(expected = InternalError.class) - public void requireNonNullPrimitive() { - ObjectHelper.requireNonNull(0, "value"); - } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java b/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java new file mode 100644 index 0000000000..67144abea2 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/fuseable/CancellableQueueFuseableTest.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.fuseable; + +import static org.junit.Assert.*; +import org.junit.Test; + +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CancellableQueueFuseableTest { + + @Test + public void offer() { + TestHelper.assertNoOffer(new CancellableQueueFuseable<>()); + } + + @Test + public void pollClear() throws Throwable { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + + assertNull(qs.poll()); + + qs.clear(); + assertNull(qs.poll()); + } + + @Test + public void cancel() { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + + assertFalse(qs.isDisposed()); + + qs.cancel(); + + assertTrue(qs.isDisposed()); + + qs.cancel(); + + assertTrue(qs.isDisposed()); + } + + @Test + public void dispose() { + CancellableQueueFuseable qs = new CancellableQueueFuseable<>(); + + assertFalse(qs.isDisposed()); + + qs.dispose(); + + assertTrue(qs.isDisposed()); + + qs.dispose(); + + assertTrue(qs.isDisposed()); + } + + @Test + public void cancel2() { + AbstractEmptyQueueFuseable qs = new AbstractEmptyQueueFuseable() { }; + + assertFalse(qs.isDisposed()); + + qs.cancel(); + } + + @Test + public void dispose2() { + AbstractEmptyQueueFuseable qs = new AbstractEmptyQueueFuseable() { }; + + assertFalse(qs.isDisposed()); + + qs.dispose(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/CollectWithCollectorTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/CollectWithCollectorTckTest.java new file mode 100644 index 0000000000..4745b9c809 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/CollectWithCollectorTckTest.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.List; +import java.util.stream.Collectors; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class CollectWithCollectorTckTest extends BaseTck> { + + @Override + public Publisher> createPublisher(final long elements) { + return + Flowable.range(0, (int)elements).collect(Collectors.toList()).toFlowable() + ; + } + + @Override + public Publisher> createFailedPublisher() { + return Flowable.error(new TestException()).collect(Collectors.toList()).toFlowable(); + } + + @Override + public long maxElementsFromPublisher() { + return 1; + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/CompletableFromCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/CompletableFromCompletionStageTest.java new file mode 100644 index 0000000000..6e9bf78e1d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/CompletableFromCompletionStageTest.java @@ -0,0 +1,78 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableFromCompletionStageTest extends RxJavaTest { + + @Test + public void syncSuccess() { + Completable.fromCompletionStage(CompletableFuture.completedFuture(1)) + .test() + .assertResult(); + } + + @Test + public void syncSuccessNull() { + Completable.fromCompletionStage(CompletableFuture.completedFuture(null)) + .test() + .assertResult(); + } + + @Test + public void syncFailure() { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new TestException()); + + Completable.fromCompletionStage(cf) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncNull() { + Completable.fromCompletionStage(CompletableFuture.completedFuture(null)) + .test() + .assertResult(); + } + + @Test + public void dispose() { + CompletableFuture cf = new CompletableFuture<>(); + + TestObserver to = Completable.fromCompletionStage(cf) + .test(); + + to.assertEmpty(); + + to.dispose(); + + cf.complete(1); + + to.assertEmpty(); + } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Completable.fromCompletionStage(new CompletableFuture<>())); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/CompletableToCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/CompletableToCompletionStageTest.java new file mode 100644 index 0000000000..8917b1c675 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/CompletableToCompletionStageTest.java @@ -0,0 +1,154 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.subjects.CompletableSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableToCompletionStageTest extends RxJavaTest { + + @Test + public void complete() throws Exception { + Object v = Completable.complete() + .toCompletionStage(null) + .toCompletableFuture() + .get(); + + assertNull(v); + } + + @Test + public void completableFutureCancels() throws Exception { + CompletableSubject source = CompletableSubject.create(); + + CompletableFuture cf = source + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void completableManualCompleteCancels() throws Exception { + CompletableSubject source = CompletableSubject.create(); + + CompletableFuture cf = source + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals(1, cf.get()); + } + + @Test + public void completableManualCompleteExceptionallyCancels() throws Exception { + CompletableSubject source = CompletableSubject.create(); + + CompletableFuture cf = source + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void error() throws Exception { + CompletableFuture cf = Completable.error(new TestException()) + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void sourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Object v = new Completable() { + @Override + protected void subscribeActual(CompletableObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .toCompletionStage(null) + .toCompletableFuture() + .get(); + + assertNull(v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void doubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Object v = new Completable() { + @Override + protected void subscribeActual(CompletableObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + } + } + .toCompletionStage(null) + .toCompletableFuture() + .get(); + + assertNull(v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream0HTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream0HTckTest.java new file mode 100644 index 0000000000..e499ac1b9a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream0HTckTest.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FlatMapStream0HTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.just(1).hide().flatMapStream(v -> IntStream.range(0, (int)elements).boxed()) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.just(1).hide().flatMapStream(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream0TckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream0TckTest.java new file mode 100644 index 0000000000..fa33b67ebf --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream0TckTest.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FlatMapStream0TckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.just(1).flatMapStream(v -> IntStream.range(0, (int)elements).boxed()) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.just(1).flatMapStream(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream1HTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream1HTckTest.java new file mode 100644 index 0000000000..ee1a27b548 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream1HTckTest.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FlatMapStream1HTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.range(1, (int)elements).hide().flatMapStream(v -> Stream.of(v)) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.just(1).hide().flatMapStream(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream1TckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream1TckTest.java new file mode 100644 index 0000000000..21492d61b8 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream1TckTest.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FlatMapStream1TckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.range(1, (int)elements).flatMapStream(v -> Stream.of(v)) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.just(1).flatMapStream(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream2HTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream2HTckTest.java new file mode 100644 index 0000000000..14e44d69ee --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream2HTckTest.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FlatMapStream2HTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + if (elements % 2 == 0) { + return Flowable.range(0, (int)elements / 2).hide().flatMapStream(v -> Stream.of(v, v + 1)); + } + return + Flowable.range(-1, 1 + (int)elements / 2).hide().flatMapStream(v -> { + if (v != -1) { + return Stream.of(v, v + 1); + } + return Stream.of(v); + }); + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.just(1).hide().flatMapStream(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream2TckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream2TckTest.java new file mode 100644 index 0000000000..c42e6f7a80 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlatMapStream2TckTest.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FlatMapStream2TckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + if (elements % 2 == 0) { + return Flowable.range(0, (int)elements / 2).flatMapStream(v -> Stream.of(v, v + 1)); + } + return + Flowable.range(-1, 1 + (int)elements / 2).flatMapStream(v -> { + if (v != -1) { + return Stream.of(v, v + 1); + } + return Stream.of(v); + }); + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.just(1).flatMapStream(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableBlockingStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableBlockingStreamTest.java new file mode 100644 index 0000000000..be49e2ed11 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableBlockingStreamTest.java @@ -0,0 +1,107 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.List; +import java.util.stream.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.processors.UnicastProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class FlowableBlockingStreamTest extends RxJavaTest { + + @Test + public void empty() { + try (Stream stream = Flowable.empty().blockingStream()) { + assertEquals(0, stream.toArray().length); + } + } + + @Test + public void just() { + try (Stream stream = Flowable.just(1).blockingStream()) { + assertArrayEquals(new Integer[] { 1 }, stream.toArray(Integer[]::new)); + } + } + + @Test + public void range() { + try (Stream stream = Flowable.range(1, 5).blockingStream()) { + assertArrayEquals(new Integer[] { 1, 2, 3, 4, 5 }, stream.toArray(Integer[]::new)); + } + } + + @Test + public void rangeBackpressured() { + try (Stream stream = Flowable.range(1, 5).blockingStream(1)) { + assertArrayEquals(new Integer[] { 1, 2, 3, 4, 5 }, stream.toArray(Integer[]::new)); + } + } + + @Test + public void rangeAsyncBackpressured() { + try (Stream stream = Flowable.range(1, 1000).subscribeOn(Schedulers.computation()).blockingStream()) { + List list = stream.collect(Collectors.toList()); + + assertEquals(1000, list.size()); + for (int i = 1; i <= 1000; i++) { + assertEquals(i, list.get(i - 1).intValue()); + } + } + } + + @Test + public void rangeAsyncBackpressured1() { + try (Stream stream = Flowable.range(1, 1000).subscribeOn(Schedulers.computation()).blockingStream(1)) { + List list = stream.collect(Collectors.toList()); + + assertEquals(1000, list.size()); + for (int i = 1; i <= 1000; i++) { + assertEquals(i, list.get(i - 1).intValue()); + } + } + } + + @Test + public void error() { + try (Stream stream = Flowable.error(new TestException()).blockingStream()) { + stream.toArray(Integer[]::new); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + } + + @Test + public void close() { + UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); + up.onNext(2); + up.onNext(3); + up.onNext(4); + up.onNext(5); + + try (Stream stream = up.blockingStream()) { + assertArrayEquals(new Integer[] { 1, 2, 3 }, stream.limit(3).toArray(Integer[]::new)); + } + + assertFalse(up.hasSubscribers()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorTest.java new file mode 100644 index 0000000000..3a8a1d5b19 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableCollectWithCollectorTest.java @@ -0,0 +1,420 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.assertFalse; + +import java.io.IOException; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class FlowableCollectWithCollectorTest extends RxJavaTest { + + @Test + public void basic() { + Flowable.range(1, 5) + .collect(Collectors.toList()) + .test() + .assertResult(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void empty() { + Flowable.empty() + .collect(Collectors.toList()) + .test() + .assertResult(Collections.emptyList()); + } + + @Test + public void error() { + Flowable.error(new TestException()) + .collect(Collectors.toList()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorSupplierCrash() { + Flowable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + throw new TestException(); + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorCrash() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void collectorFinisherCrash() { + Flowable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> { throw new TestException(); }; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorDropSignals() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + s.onError(new IOException()); + s.onComplete(); + } + }; + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(PublishProcessor.create() + .collect(Collectors.toList())); + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToSingle(f -> f.collect(Collectors.toList())); + } + + @Test + public void basicToFlowable() { + Flowable.range(1, 5) + .collect(Collectors.toList()) + .toFlowable() + .test() + .assertResult(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void emptyToFlowable() { + Flowable.empty() + .collect(Collectors.toList()) + .toFlowable() + .test() + .assertResult(Collections.emptyList()); + } + + @Test + public void errorToFlowable() { + Flowable.error(new TestException()) + .collect(Collectors.toList()) + .toFlowable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorSupplierCrashToFlowable() { + Flowable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + throw new TestException(); + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toFlowable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorCrashToFlowable() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toFlowable() + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void collectorFinisherCrashToFlowable() { + Flowable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> { throw new TestException(); }; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toFlowable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorDropSignalsToFlowable() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + s.onError(new IOException()); + s.onComplete(); + } + }; + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toFlowable() + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void disposeToFlowable() { + TestHelper.checkDisposed(PublishProcessor.create() + .collect(Collectors.toList()).toFlowable()); + } + + @Test + public void onSubscribeToFlowable() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.collect(Collectors.toList()).toFlowable()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFlatMapStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFlatMapStreamTest.java new file mode 100644 index 0000000000..c865d35a52 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFlatMapStreamTest.java @@ -0,0 +1,451 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.*; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class FlowableFlatMapStreamTest extends RxJavaTest { + + @Test + public void empty() { + Flowable.empty() + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertResult(); + } + + @Test + public void emptyHidden() { + Flowable.empty() + .hide() + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertResult(); + } + + @Test + public void just() { + Flowable.just(1) + .flatMapStream(v -> Stream.of(v + 1, v + 2, v + 3, v + 4, v + 5)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void justHidden() { + Flowable.just(1).hide() + .flatMapStream(v -> Stream.of(v + 1, v + 2, v + 3, v + 4, v + 5)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void error() { + Flowable.error(new TestException()) + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void supplierFusedError() { + Flowable.fromCallable(() -> { throw new TestException(); }) + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void errorHidden() { + Flowable.error(new TestException()) + .hide() + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void range() { + Flowable.range(1, 5) + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31, 32, 33, 34, + 40, 41, 42, 43, 44, + 50, 51, 52, 53, 54 + ); + } + + @Test + public void rangeHidden() { + Flowable.range(1, 5) + .hide() + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31, 32, 33, 34, + 40, 41, 42, 43, 44, + 50, 51, 52, 53, 54 + ); + } + + @Test + public void rangeToEmpty() { + Flowable.range(1, 5) + .flatMapStream(v -> Stream.of()) + .test() + .assertResult(); + } + + @Test + public void rangeTake() { + Flowable.range(1, 5) + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .take(12) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31 + ); + } + + @Test + public void rangeTakeHidden() { + Flowable.range(1, 5) + .hide() + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .take(12) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31 + ); + } + + @Test + public void upstreamCancelled() { + PublishProcessor pp = PublishProcessor.create(); + + AtomicInteger calls = new AtomicInteger(); + + TestSubscriber ts = pp + .flatMapStream(v -> Stream.of(v + 1, v + 2).onClose(() -> calls.getAndIncrement())) + .test(1); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.assertValuesOnly(2); + ts.cancel(); + + assertFalse(pp.hasSubscribers()); + + assertEquals(1, calls.get()); + } + + @Test + public void upstreamCancelledCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp + .flatMapStream(v -> Stream.of(v + 1, v + 2).onClose(() -> { throw new TestException(); })) + .test(1); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.assertValuesOnly(2); + ts.cancel(); + + assertFalse(pp.hasSubscribers()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void crossMap() { + Flowable.range(1, 1000) + .flatMapStream(v -> IntStream.range(v * 1000, v * 1000 + 1000).boxed()) + .test() + .assertValueCount(1_000_000) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void crossMapHidden() { + Flowable.range(1, 1000) + .hide() + .flatMapStream(v -> IntStream.range(v * 1000, v * 1000 + 1000).boxed()) + .test() + .assertValueCount(1_000_000) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void crossMapBackpressured() { + for (int n = 1; n < 2048; n *= 2) { + Flowable.range(1, 1000) + .flatMapStream(v -> IntStream.range(v * 1000, v * 1000 + 1000).boxed()) + .rebatchRequests(n) + .test() + .withTag("rebatch: " + n) + .assertValueCount(1_000_000) + .assertNoErrors() + .assertComplete(); + } + } + + @Test + public void crossMapBackpressuredHidden() { + for (int n = 1; n < 2048; n *= 2) { + Flowable.range(1, 1000) + .hide() + .flatMapStream(v -> IntStream.range(v * 1000, v * 1000 + 1000).boxed()) + .rebatchRequests(n) + .test() + .withTag("rebatch: " + n) + .assertValueCount(1_000_000) + .assertNoErrors() + .assertComplete(); + } + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.flatMapStream(v -> Stream.of(1, 2))); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(UnicastProcessor.create().flatMapStream(v -> Stream.of(1, 2))); + } + + @Test + public void queueOverflow() throws Throwable { + TestHelper.withErrorTracking(errors -> { + new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + s.onNext(3); + s.onError(new TestException()); + } + } + .flatMapStream(v -> Stream.of(1, 2), 1) + .test(0) + .assertFailure(QueueOverflowException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void mapperThrows() { + Flowable.just(1).hide() + .concatMapStream(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperNull() { + Flowable.just(1).hide() + .concatMapStream(v -> null) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void streamNull() { + Flowable.just(1).hide() + .concatMapStream(v -> Stream.of(1, null)) + .test() + .assertFailure(NullPointerException.class, 1); + } + + @Test + public void hasNextThrows() { + Flowable.just(1).hide() + .concatMapStream(v -> Stream.generate(() -> { throw new TestException(); })) + .test() + .assertFailure(TestException.class); + } + + @Test + public void hasNextThrowsLater() { + AtomicInteger counter = new AtomicInteger(); + Flowable.just(1).hide() + .concatMapStream(v -> Stream.generate(() -> { + if (counter.getAndIncrement() == 0) { + return 1; + } + throw new TestException(); + })) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void mapperThrowsWhenUpstreamErrors() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor pp = PublishProcessor.create(); + + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber ts = pp.hide() + .concatMapStream(v -> { + if (counter.getAndIncrement() == 0) { + return Stream.of(1, 2); + } + pp.onError(new IOException()); + throw new TestException(); + }) + .test(); + + pp.onNext(1); + pp.onNext(2); + + ts + .assertFailure(IOException.class, 1, 2); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void rangeBackpressured() { + Flowable.range(1, 5) + .hide() + .concatMapStream(v -> Stream.of(v), 1) + .test(0) + .assertEmpty() + .requestMore(5) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void cancelAfterIteratorNext() throws Exception { + TestSubscriber ts = new TestSubscriber<>(); + + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + }); + + Flowable.just(1) + .hide() + .concatMapStream(v -> stream) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void asyncUpstreamFused() { + UnicastProcessor up = UnicastProcessor.create(); + + TestSubscriber ts = up.flatMapStream(v -> Stream.of(1, 2)) + .test(); + + assertTrue(up.hasSubscribers()); + + up.onNext(1); + + ts.assertValuesOnly(1, 2); + + up.onComplete(); + + ts.assertResult(1, 2); + } + + @Test + public void asyncUpstreamFusionBoundary() { + UnicastProcessor up = UnicastProcessor.create(); + + TestSubscriber ts = up + .map(v -> v + 1) + .flatMapStream(v -> Stream.of(1, 2)) + .test(); + + assertTrue(up.hasSubscribers()); + + up.onNext(1); + + ts.assertValuesOnly(1, 2); + + up.onComplete(); + + ts.assertResult(1, 2); + } + + @Test + public void fusedPollCrash() { + UnicastProcessor up = UnicastProcessor.create(); + + TestSubscriber ts = up + .map(v -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()) + .flatMapStream(v -> Stream.of(1, 2)) + .test(); + + assertTrue(up.hasSubscribers()); + + up.onNext(1); + + assertFalse(up.hasSubscribers()); + + ts.assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromCompletionStageTest.java new file mode 100644 index 0000000000..aebeea58b6 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromCompletionStageTest.java @@ -0,0 +1,65 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.subscribers.TestSubscriber; + +public class FlowableFromCompletionStageTest extends RxJavaTest { + + @Test + public void syncSuccess() { + Flowable.fromCompletionStage(CompletableFuture.completedFuture(1)) + .test() + .assertResult(1); + } + + @Test + public void syncFailure() { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new TestException()); + + Flowable.fromCompletionStage(cf) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncNull() { + Flowable.fromCompletionStage(CompletableFuture.completedFuture(null)) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void cancel() { + CompletableFuture cf = new CompletableFuture<>(); + + TestSubscriber ts = Flowable.fromCompletionStage(cf) + .test(); + + ts.assertEmpty(); + + ts.cancel(); + + cf.complete(1); + + ts.assertEmpty(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromOptionalTest.java new file mode 100644 index 0000000000..743dc0f573 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromOptionalTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Optional; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; + +public class FlowableFromOptionalTest extends RxJavaTest { + + @Test + public void hasValue() { + Flowable.fromOptional(Optional.of(1)) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Flowable.fromOptional(Optional.empty()) + .test() + .assertResult(); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromStreamTest.java new file mode 100644 index 0000000000..57a8caf6bf --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableFromStreamTest.java @@ -0,0 +1,559 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.stream.*; + +import org.junit.Test; +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.*; + +public class FlowableFromStreamTest extends RxJavaTest { + + @Test + public void empty() { + Flowable.fromStream(Stream.of()) + .test() + .assertResult(); + } + + @Test + public void just() { + Flowable.fromStream(Stream.of(1)) + .test() + .assertResult(1); + } + + @Test + public void many() { + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5)) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void manyBackpressured() { + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5)) + .test(0L) + .assertEmpty() + .requestMore(1) + .assertValuesOnly(1) + .requestMore(2) + .assertValuesOnly(1, 2, 3) + .requestMore(2) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void noReuse() { + Flowable source = Flowable.fromStream(Stream.of(1, 2, 3, 4, 5)); + + source + .test() + .assertResult(1, 2, 3, 4, 5); + + source + .test() + .assertFailure(IllegalStateException.class); + } + + @Test + public void take() { + Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .take(5) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void emptyConditional() { + Flowable.fromStream(Stream.of()) + .filter(v -> true) + .test() + .assertResult(); + } + + @Test + public void justConditional() { + Flowable.fromStream(Stream.of(1)) + .filter(v -> true) + .test() + .assertResult(1); + } + + @Test + public void manyConditional() { + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5)) + .filter(v -> true) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void manyBackpressuredConditional() { + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5)) + .filter(v -> true) + .test(0L) + .assertEmpty() + .requestMore(1) + .assertValuesOnly(1) + .requestMore(2) + .assertValuesOnly(1, 2, 3) + .requestMore(2) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void manyConditionalSkip() { + Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .filter(v -> v % 2 == 0) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void takeConditional() { + Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .filter(v -> true) + .take(5) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void noOfferNoCrashAfterClear() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + + Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .subscribe(new FlowableSubscriber() { + @Override + public void onSubscribe(@NonNull Subscription s) { + queue.set((SimpleQueue)s); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + TestHelper.assertNoOffer(q); + + assertFalse(q.isEmpty()); + + q.clear(); + + assertNull(q.poll()); + + assertTrue(q.isEmpty()); + + q.clear(); + + assertNull(q.poll()); + + assertTrue(q.isEmpty()); + } + + @Test + public void fusedPoll() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + AtomicInteger calls = new AtomicInteger(); + + Flowable.fromStream(Stream.of(1).onClose(() -> calls.getAndIncrement())) + .subscribe(new FlowableSubscriber() { + @Override + public void onSubscribe(@NonNull Subscription s) { + queue.set((SimpleQueue)s); + ((QueueSubscription)s).requestFusion(QueueFuseable.ANY); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + + assertFalse(q.isEmpty()); + + assertEquals(1, q.poll()); + + assertTrue(q.isEmpty()); + + assertEquals(1, calls.get()); + } + + @Test + public void streamOfNull() { + Flowable.fromStream(Stream.of((Integer)null)) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void streamOfNullConditional() { + Flowable.fromStream(Stream.of((Integer)null)) + .filter(v -> true) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void syncFusionSupport() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void asyncFusionNotSupported() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ASYNC); + + Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void fusedForParallel() { + Flowable.fromStream(IntStream.rangeClosed(1, 1000).boxed()) + .parallel() + .runOn(Schedulers.computation(), 1) + .map(v -> v + 1) + .sequential() + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertValueCount(1000) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void runToEndCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Stream stream = Stream.of(1, 2, 3, 4, 5).onClose(() -> { throw new TestException(); }); + + Flowable.fromStream(stream) + .test() + .assertResult(1, 2, 3, 4, 5); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void takeCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Stream stream = Stream.of(1, 2, 3, 4, 5).onClose(() -> { throw new TestException(); }); + + Flowable.fromStream(stream) + .take(3) + .test() + .assertResult(1, 2, 3); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void hasNextCrash() { + AtomicInteger v = new AtomicInteger(); + Flowable.fromStream(Stream.generate(() -> { + int value = v.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + })) + .test() + .assertFailure(TestException.class, 0); + } + + @Test + public void hasNextCrashConditional() { + AtomicInteger counter = new AtomicInteger(); + Flowable.fromStream(Stream.generate(() -> { + int value = counter.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + })) + .filter(v -> true) + .test() + .assertFailure(TestException.class, 0); + } + + void requestOneByOneBase(boolean conditional) { + List list = new ArrayList<>(); + + Flowable source = Flowable.fromStream(IntStream.rangeClosed(1, 10).boxed()); + if (conditional) { + source = source.filter(v -> true); + } + + source.subscribe(new FlowableSubscriber() { + + @NonNull Subscription upstream; + + @Override + public void onSubscribe(@NonNull Subscription s) { + this.upstream = s; + s.request(1); + } + + @Override + public void onNext(Integer t) { + list.add(t); + upstream.request(1); + } + + @Override + public void onError(Throwable t) { + list.add(t); + } + + @Override + public void onComplete() { + list.add(100); + } + }); + + assertEquals(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100), list); + } + + @Test + public void requestOneByOne() { + requestOneByOneBase(false); + } + + @Test + public void requestOneByOneConditional() { + requestOneByOneBase(true); + } + + void requestRaceBase(boolean conditional) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + try { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + AtomicInteger counter = new AtomicInteger(); + + int max = 100; + + Flowable source = Flowable.fromStream(IntStream.rangeClosed(1, max).boxed()); + if (conditional) { + source = source.filter(v -> true); + } + + CountDownLatch cdl = new CountDownLatch(1); + + source + .subscribe(new FlowableSubscriber() { + + @NonNull Subscription upstream; + + @Override + public void onSubscribe(@NonNull Subscription s) { + + this.upstream = s; + s.request(1); + + } + + @Override + public void onNext(Integer t) { + counter.getAndIncrement(); + + AtomicInteger sync = new AtomicInteger(2); + exec.submit(() -> { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + upstream.request(1); + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + cdl.countDown(); + } + + @Override + public void onComplete() { + counter.getAndIncrement(); + cdl.countDown(); + } + }); + + assertTrue(cdl.await(60, TimeUnit.SECONDS)); + + assertEquals(max + 1, counter.get()); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void requestRace() throws Exception { + requestRaceBase(false); + } + + @Test + public void requestRaceConditional() throws Exception { + requestRaceBase(true); + } + + @Test + public void closeCalledOnEmpty() { + AtomicInteger calls = new AtomicInteger(); + + Flowable.fromStream(Stream.of().onClose(() -> calls.getAndIncrement())) + .test() + .assertResult(); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledAfterItems() { + AtomicInteger calls = new AtomicInteger(); + + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .test() + .assertResult(1, 2, 3, 4, 5); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnCancel() { + AtomicInteger calls = new AtomicInteger(); + + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .take(3) + .test() + .assertResult(1, 2, 3); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnItemCrash() { + AtomicInteger calls = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + Flowable.fromStream(Stream.generate(() -> { + int value = counter.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + }).onClose(() -> calls.getAndIncrement())) + .test() + .assertFailure(TestException.class, 0); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledAfterItemsConditional() { + AtomicInteger calls = new AtomicInteger(); + + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .filter(v -> true) + .test() + .assertResult(1, 2, 3, 4, 5); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnCancelConditional() { + AtomicInteger calls = new AtomicInteger(); + + Flowable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .filter(v -> true) + .take(3) + .test() + .assertResult(1, 2, 3); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnItemCrashConditional() { + AtomicInteger calls = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + Flowable.fromStream(Stream.generate(() -> { + int value = counter.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + }).onClose(() -> calls.getAndIncrement())) + .filter(v -> true) + .test() + .assertFailure(TestException.class, 0); + + assertEquals(1, calls.get()); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.fromStream(Stream.of(1))); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java new file mode 100644 index 0000000000..fbffd79001 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableMapOptionalTest.java @@ -0,0 +1,487 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.assertFalse; + +import java.util.Optional; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.testsupport.*; + +public class FlowableMapOptionalTest extends RxJavaTest { + + static final Function> MODULO = v -> v % 2 == 0 ? Optional.of(v) : Optional.empty(); + + @Test + public void allPresent() { + Flowable.range(1, 5) + .mapOptional(Optional::of) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void allEmpty() { + Flowable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .test() + .assertResult(); + } + + @Test + public void mixed() { + Flowable.range(1, 10) + .mapOptional(MODULO) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void mapperChash() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .mapOptional(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void mapperNull() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .mapOptional(v -> null) + .test() + .assertFailure(NullPointerException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void crashDropsOnNexts() { + Flowable source = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + } + }; + + source + .mapOptional(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void backpressureAll() { + Flowable.range(1, 5) + .mapOptional(Optional::of) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(1, 2) + .requestMore(2) + .assertValuesOnly(1, 2, 3, 4) + .requestMore(1) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void backpressureNone() { + Flowable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .test(1L) + .assertResult(); + } + + @Test + public void backpressureMixed() { + Flowable.range(1, 10) + .mapOptional(MODULO) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(2, 4) + .requestMore(2) + .assertValuesOnly(2, 4, 6, 8) + .requestMore(1) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void syncFusedAll() { + Flowable.range(1, 5) + .mapOptional(Optional::of) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void asyncFusedAll() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(Optional::of) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void boundaryFusedAll() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(Optional::of) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void syncFusedNone() { + Flowable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(); + } + + @Test + public void asyncFusedNone() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(v -> Optional.empty()) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void boundaryFusedNone() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(v -> Optional.empty()) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(); + } + + @Test + public void syncFusedMixed() { + Flowable.range(1, 10) + .mapOptional(MODULO) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void asyncFusedMixed() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + up + .mapOptional(MODULO) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void boundaryFusedMixed() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + up + .mapOptional(MODULO) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void allPresentConditional() { + Flowable.range(1, 5) + .mapOptional(Optional::of) + .filter(v -> true) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void allEmptyConditional() { + Flowable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .test() + .assertResult(); + } + + @Test + public void mixedConditional() { + Flowable.range(1, 10) + .mapOptional(MODULO) + .filter(v -> true) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void mapperChashConditional() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .mapOptional(v -> { throw new TestException(); }) + .filter(v -> true) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void mapperNullConditional() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .mapOptional(v -> null) + .filter(v -> true) + .test() + .assertFailure(NullPointerException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void crashDropsOnNextsConditional() { + Flowable source = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + } + }; + + source + .mapOptional(v -> { throw new TestException(); }) + .filter(v -> true) + .test() + .assertFailure(TestException.class); + } + + @Test + public void backpressureAllConditional() { + Flowable.range(1, 5) + .mapOptional(Optional::of) + .filter(v -> true) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(1, 2) + .requestMore(2) + .assertValuesOnly(1, 2, 3, 4) + .requestMore(1) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void backpressureNoneConditional() { + Flowable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .test(1L) + .assertResult(); + } + + @Test + public void backpressureMixedConditional() { + Flowable.range(1, 10) + .mapOptional(MODULO) + .filter(v -> true) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(2, 4) + .requestMore(2) + .assertValuesOnly(2, 4, 6, 8) + .requestMore(1) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void syncFusedAllConditional() { + Flowable.range(1, 5) + .mapOptional(Optional::of) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void asyncFusedAllConditional() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(Optional::of) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void boundaryFusedAllConditiona() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(Optional::of) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void syncFusedNoneConditional() { + Flowable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(); + } + + @Test + public void asyncFusedNoneConditional() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void boundaryFusedNoneConditional() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(); + } + + @Test + public void syncFusedMixedConditional() { + Flowable.range(1, 10) + .mapOptional(MODULO) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void asyncFusedMixedConditional() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + up + .mapOptional(MODULO) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void boundaryFusedMixedConditional() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + up + .mapOptional(MODULO) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void conditionalFusionNoNPE() { + TestSubscriberEx ts = new TestSubscriberEx<>() + .setInitialFusionMode(QueueFuseable.ANY); + + Flowable.empty() + .observeOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + .mapOptional(Optional::of) + .filter(v -> true) + .subscribe(ts) + ; + + ts.assertResult(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriberOrDefaultTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriberOrDefaultTest.java new file mode 100644 index 0000000000..d63a81c8ef --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriberOrDefaultTest.java @@ -0,0 +1,476 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.concurrent.*; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class FlowableStageSubscriberOrDefaultTest extends RxJavaTest { + + @Test + public void firstJust() throws Exception { + Integer v = Flowable.just(1) + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void firstEmpty() throws Exception { + Integer v = Flowable.empty() + .firstStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void firstCancels() throws Exception { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + Integer v = source + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void firstCompletableFutureCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .firstStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void firstCompletableManualCompleteCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .firstStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void firstCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .firstStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstError() throws Exception { + CompletableFuture cf = Flowable.error(new TestException()) + .firstStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onError(new TestException()); + s.onComplete(); + } + } + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void firstDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + } + } + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void singleJust() throws Exception { + Integer v = Flowable.just(1) + .singleStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void singleEmpty() throws Exception { + Integer v = Flowable.empty() + .singleStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void singleTooManyCancels() throws Exception { + ReplayProcessor source = ReplayProcessor.create(); + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(source + .singleStage(null) + .toCompletableFuture(), IllegalArgumentException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void singleCompletableFutureCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .singleStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void singleCompletableManualCompleteCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .singleStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void singleCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .singleStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleError() throws Exception { + CompletableFuture cf = Flowable.error(new TestException()) + .singleStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + s.onError(new TestException()); + s.onComplete(); + } + } + .singleStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void singleDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + } + } + .singleStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void lastJust() throws Exception { + Integer v = Flowable.just(1) + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void lastRange() throws Exception { + Integer v = Flowable.range(1, 5) + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)5, v); + } + + @Test + public void lastEmpty() throws Exception { + Integer v = Flowable.empty() + .lastStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void lastCompletableFutureCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .lastStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void lastCompletableManualCompleteCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .lastStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void lastCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .lastStage(null) + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastError() throws Exception { + CompletableFuture cf = Flowable.error(new TestException()) + .lastStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + s.onError(new TestException()); + s.onComplete(); + } + } + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void lastDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + } + } + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriberOrErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriberOrErrorTest.java new file mode 100644 index 0000000000..763a1e0444 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FlowableStageSubscriberOrErrorTest.java @@ -0,0 +1,470 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.NoSuchElementException; +import java.util.concurrent.*; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class FlowableStageSubscriberOrErrorTest extends RxJavaTest { + + @Test + public void firstJust() throws Exception { + Integer v = Flowable.just(1) + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void firstEmpty() throws Exception { + TestHelper.assertError( + Flowable.empty() + .firstOrErrorStage() + .toCompletableFuture(), NoSuchElementException.class); + } + + @Test + public void firstCancels() throws Exception { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + Integer v = source + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void firstCompletableFutureCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void firstCompletableManualCompleteCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void firstCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstError() throws Exception { + CompletableFuture cf = Flowable.error(new TestException()) + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onError(new TestException()); + s.onComplete(); + } + } + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void firstDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + } + } + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void singleJust() throws Exception { + Integer v = Flowable.just(1) + .singleOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void singleEmpty() throws Exception { + TestHelper.assertError( + Flowable.empty() + .singleOrErrorStage() + .toCompletableFuture(), NoSuchElementException.class); + } + + @Test + public void singleTooManyCancels() throws Exception { + ReplayProcessor source = ReplayProcessor.create(); + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(source + .singleOrErrorStage() + .toCompletableFuture(), IllegalArgumentException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void singleCompletableFutureCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void singleCompletableManualCompleteCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void singleCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleError() throws Exception { + CompletableFuture cf = Flowable.error(new TestException()) + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + s.onError(new TestException()); + s.onComplete(); + } + } + .singleOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void singleDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + } + } + .singleOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void lastJust() throws Exception { + Integer v = Flowable.just(1) + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void lastRange() throws Exception { + Integer v = Flowable.range(1, 5) + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)5, v); + } + + @Test + public void lastEmpty() throws Exception { + TestHelper.assertError(Flowable.empty() + .lastOrErrorStage() + .toCompletableFuture(), NoSuchElementException.class); + } + + @Test + public void lastCompletableFutureCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void lastCompletableManualCompleteCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void lastCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishProcessor source = PublishProcessor.create(); + + CompletableFuture cf = source + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasSubscribers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasSubscribers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastError() throws Exception { + CompletableFuture cf = Flowable.error(new TestException()) + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + s.onError(new TestException()); + s.onComplete(); + } + } + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void lastDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Flowable() { + @Override + protected void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + } + } + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromCompletionStageTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromCompletionStageTckTest.java new file mode 100644 index 0000000000..6d2e9d2155 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromCompletionStageTckTest.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class FromCompletionStageTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.fromCompletionStage(CompletableFuture.completedFuture(1L)) + ; + } + + @Override + public Publisher createFailedPublisher() { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new TestException()); + return + Flowable.fromCompletionStage(cf) + ; + } + + @Override + public long maxElementsFromPublisher() { + return 1; + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromOptional0TckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromOptional0TckTest.java new file mode 100644 index 0000000000..ff30aa4b2d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromOptional0TckTest.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Optional; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +/** + * Test Optional.empty() wrapping. + * @see FromOptional1TckTest + */ +@Test +public class FromOptional0TckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.fromOptional(Optional.empty()) + ; + } + + @Override + public long maxElementsFromPublisher() { + return 0; + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromOptional1TckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromOptional1TckTest.java new file mode 100644 index 0000000000..f6b56a582d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromOptional1TckTest.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Optional; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +/** + * Test Optional.of wrapping. + * @see FromOptional0TckTest + */ +@Test +public class FromOptional1TckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.fromOptional(Optional.of(1L)) + ; + } + + @Override + public long maxElementsFromPublisher() { + return 1; + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromStreamTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromStreamTckTest.java new file mode 100644 index 0000000000..a0995dad39 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/FromStreamTckTest.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +/** + * Test Optional.of wrapping. + * @see FromOptional0TckTest + */ +@Test +public class FromStreamTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.fromStream(IntStream.rangeClosed(1, (int)elements).boxed()) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Flowable.fromStream(stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MapOptionalTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MapOptionalTckTest.java new file mode 100644 index 0000000000..345159da4a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MapOptionalTckTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Optional; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class MapOptionalTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Flowable.range(0, (int)(2 * elements)).mapOptional(v -> v % 2 == 0 ? Optional.of(v) : Optional.empty()) + ; + } + + @Override + public Publisher createFailedPublisher() { + return Flowable.just(1).mapOptional(v -> null).onBackpressureDrop(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowableTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowableTckTest.java new file mode 100644 index 0000000000..55e6778d3f --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowableTckTest.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class MaybeFlattenStreamAsFlowableTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Maybe.just(1).flattenStreamAsFlowable(v -> IntStream.range(0, (int)elements).boxed()) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Maybe.just(1).flattenStreamAsFlowable(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowableTest.java new file mode 100644 index 0000000000..d8377eac60 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsFlowableTest.java @@ -0,0 +1,454 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.*; + +import org.junit.Test; +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class MaybeFlattenStreamAsFlowableTest extends RxJavaTest { + + @Test + public void successJust() { + Maybe.just(1) + .flattenStreamAsFlowable(Stream::of) + .test() + .assertResult(1); + } + + @Test + public void successEmpty() { + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of()) + .test() + .assertResult(); + } + + @Test + public void successMany() { + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(2, 3, 4, 5, 6)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void successManyTake() { + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(2, 3, 4, 5, 6)) + .take(3) + .test() + .assertResult(2, 3, 4); + } + + @Test + public void empty() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Maybe.empty() + .flattenStreamAsFlowable(f) + .test() + .assertResult(); + + verify(f, never()).apply(any()); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Maybe.error(new TestException()) + .flattenStreamAsFlowable(f) + .test() + .assertFailure(TestException.class); + + verify(f, never()).apply(any()); + } + + @Test + public void mapperCrash() { + Maybe.just(1) + .flattenStreamAsFlowable(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Maybe.never().flattenStreamAsFlowable(Stream::of)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybeToFlowable(m -> m.flattenStreamAsFlowable(Stream::of)); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(MaybeSubject.create().flattenStreamAsFlowable(Stream::of)); + } + + @Test + public void fusedEmpty() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of()) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void fusedJust() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void fusedMany() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3); + } + + @Test + public void fusedManyRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3); + } + + @Test + public void manyBackpressured() { + Maybe.just(1) + .flattenStreamAsFlowable(v -> IntStream.rangeClosed(1, 5).boxed()) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(1, 2) + .requestMore(2) + .assertValuesOnly(1, 2, 3, 4) + .requestMore(1) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void manyBackpressured2() { + Maybe.just(1) + .flattenStreamAsFlowable(v -> IntStream.rangeClosed(1, 5).boxed()) + .rebatchRequests(1) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(1, 2) + .requestMore(2) + .assertValuesOnly(1, 2, 3, 4) + .requestMore(1) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void fusedStreamAvailableLater() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + MaybeSubject ms = MaybeSubject.create(); + + ms + .flattenStreamAsFlowable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertEmpty(); + + ms.onSuccess(1); + + ts + .assertResult(1, 2, 3); + } + + @Test + public void fused() throws Throwable { + AtomicReference> qsr = new AtomicReference<>(); + + MaybeSubject ms = MaybeSubject.create(); + + ms + .flattenStreamAsFlowable(Stream::of) + .subscribe(new FlowableSubscriber() { + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + @SuppressWarnings("unchecked") + public void onSubscribe(@NonNull Subscription s) { + qsr.set((QueueSubscription)s); + } + }); + + QueueSubscription qs = qsr.get(); + + assertEquals(QueueFuseable.ASYNC, qs.requestFusion(QueueFuseable.ASYNC)); + + assertTrue(qs.isEmpty()); + assertNull(qs.poll()); + + ms.onSuccess(1); + + assertFalse(qs.isEmpty()); + assertEquals(1, qs.poll().intValue()); + + assertTrue(qs.isEmpty()); + assertNull(qs.poll()); + + qs.cancel(); + + assertTrue(qs.isEmpty()); + assertNull(qs.poll()); + } + + @Test + public void requestOneByOne() { + TestSubscriber ts = new TestSubscriber<>(); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(1, 2, 3, 4, 5)) + .subscribe(new FlowableSubscriber() { + + Subscription upstream; + + @Override + public void onSubscribe(@NonNull Subscription s) { + ts.onSubscribe(new BooleanSubscription()); + upstream = s; + s.request(1); + } + + @Override + public void onNext(Integer t) { + ts.onNext(t); + upstream.request(1); + } + + @Override + public void onError(Throwable t) { + ts.onError(t); + } + + @Override + public void onComplete() { + ts.onComplete(); + } + }); + + ts.assertResult(1, 2, 3, 4, 5); + } + + @Test + public void streamCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Maybe.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v).onClose(() -> { throw new TestException(); })) + .test() + .assertResult(1); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void hasNextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + throw new TestException(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> stream) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void nextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + throw new TestException(); + } + }); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> stream) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelAfterHasNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestSubscriber ts = new TestSubscriber<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + ts.cancel(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> stream) + .subscribeWith(ts) + .assertValuesOnly(1); + } + + @Test + public void cancelAfterNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestSubscriber ts = new TestSubscriber<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + }); + + Maybe.just(1) + .flattenStreamAsFlowable(v -> stream) + .subscribeWith(ts) + .assertEmpty(); + } + + @Test + public void requestSuccessRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + MaybeSubject ms = MaybeSubject.create(); + + TestSubscriber ts = new TestSubscriber<>(0L); + + ms.flattenStreamAsFlowable(Stream::of) + .subscribe(ts); + + Runnable r1 = () -> ms.onSuccess(1); + Runnable r2 = () -> ts.request(1); + + TestHelper.race(r1, r2); + + ts.assertResult(1); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsObservableTest.java new file mode 100644 index 0000000000..145112987d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFlattenStreamAsObservableTest.java @@ -0,0 +1,432 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.*; + +public class MaybeFlattenStreamAsObservableTest extends RxJavaTest { + + @Test + public void successJust() { + Maybe.just(1) + .flattenStreamAsObservable(Stream::of) + .test() + .assertResult(1); + } + + @Test + public void successEmpty() { + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of()) + .test() + .assertResult(); + } + + @Test + public void successMany() { + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of(2, 3, 4, 5, 6)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void successManyTake() { + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of(2, 3, 4, 5, 6)) + .take(3) + .test() + .assertResult(2, 3, 4); + } + + @Test + public void empty() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Maybe.empty() + .flattenStreamAsObservable(f) + .test() + .assertResult(); + + verify(f, never()).apply(any()); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Maybe.error(new TestException()) + .flattenStreamAsObservable(f) + .test() + .assertFailure(TestException.class); + + verify(f, never()).apply(any()); + } + + @Test + public void mapperCrash() { + Maybe.just(1) + .flattenStreamAsObservable(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Maybe.never().flattenStreamAsObservable(Stream::of)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybeToObservable(m -> m.flattenStreamAsObservable(Stream::of)); + } + + @Test + public void fusedEmpty() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of()) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void fusedJust() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of(v)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void fusedMany() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3); + } + + @Test + public void fusedManyRejected() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3); + } + + @Test + public void fusedStreamAvailableLater() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + MaybeSubject ms = MaybeSubject.create(); + + ms + .flattenStreamAsObservable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertEmpty(); + + ms.onSuccess(1); + + to + .assertResult(1, 2, 3); + } + + @Test + public void fused() throws Throwable { + AtomicReference> qdr = new AtomicReference<>(); + + MaybeSubject ms = MaybeSubject.create(); + + ms + .flattenStreamAsObservable(Stream::of) + .subscribe(new Observer() { + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + @SuppressWarnings("unchecked") + public void onSubscribe(Disposable d) { + qdr.set((QueueDisposable)d); + } + }); + + QueueDisposable qd = qdr.get(); + + assertEquals(QueueFuseable.ASYNC, qd.requestFusion(QueueFuseable.ASYNC)); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + ms.onSuccess(1); + + assertFalse(qd.isEmpty()); + assertEquals(1, qd.poll().intValue()); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + qd.dispose(); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + } + + @Test + public void fused2() throws Throwable { + AtomicReference> qdr = new AtomicReference<>(); + + MaybeSubject ms = MaybeSubject.create(); + + ms + .flattenStreamAsObservable(v -> Stream.of(v, v + 1)) + .subscribe(new Observer() { + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + @SuppressWarnings("unchecked") + public void onSubscribe(Disposable d) { + qdr.set((QueueDisposable)d); + } + }); + + QueueDisposable qd = qdr.get(); + + assertEquals(QueueFuseable.ASYNC, qd.requestFusion(QueueFuseable.ASYNC)); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + ms.onSuccess(1); + + assertFalse(qd.isEmpty()); + assertEquals(1, qd.poll().intValue()); + + assertFalse(qd.isEmpty()); + assertEquals(2, qd.poll().intValue()); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + qd.dispose(); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + } + + @Test + public void streamCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Maybe.just(1) + .flattenStreamAsObservable(v -> Stream.of(v).onClose(() -> { throw new TestException(); })) + .test() + .assertResult(1); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void hasNextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + throw new TestException(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Maybe.just(1) + .flattenStreamAsObservable(v -> stream) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void nextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + throw new TestException(); + } + }); + + Maybe.just(1) + .flattenStreamAsObservable(v -> stream) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelAfterHasNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestObserver to = new TestObserver<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + to.dispose(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Maybe.just(1) + .flattenStreamAsObservable(v -> stream) + .subscribeWith(to) + .assertValuesOnly(1); + } + + @Test + public void cancelAfterNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestObserver to = new TestObserver<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + to.dispose(); + return 1; + } + }); + + Maybe.just(1) + .flattenStreamAsObservable(v -> stream) + .subscribeWith(to) + .assertEmpty(); + } + + @Test + public void cancelSuccessRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + MaybeSubject ms = MaybeSubject.create(); + + TestObserver to = new TestObserver<>(); + + ms.flattenStreamAsObservable(Stream::of) + .subscribe(to); + + Runnable r1 = () -> ms.onSuccess(1); + Runnable r2 = () -> to.dispose(); + + TestHelper.race(r1, r2); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromCompletionStageTest.java new file mode 100644 index 0000000000..552a4afc10 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromCompletionStageTest.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeFromCompletionStageTest extends RxJavaTest { + + @Test + public void syncSuccess() { + Maybe.fromCompletionStage(CompletableFuture.completedFuture(1)) + .test() + .assertResult(1); + } + + @Test + public void syncFailure() { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new TestException()); + + Maybe.fromCompletionStage(cf) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncNull() { + Maybe.fromCompletionStage(CompletableFuture.completedFuture(null)) + .test() + .assertResult(); + } + + @Test + public void dispose() { + CompletableFuture cf = new CompletableFuture<>(); + + TestObserver to = Maybe.fromCompletionStage(cf) + .test(); + + to.assertEmpty(); + + to.dispose(); + + cf.complete(1); + + to.assertEmpty(); + } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Maybe.fromCompletionStage(new CompletableFuture<>())); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromOptionalTest.java new file mode 100644 index 0000000000..8af5faf5d7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeFromOptionalTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Optional; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; + +public class MaybeFromOptionalTest extends RxJavaTest { + + @Test + public void hasValue() { + Maybe.fromOptional(Optional.of(1)) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Maybe.fromOptional(Optional.empty()) + .test() + .assertResult(); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeMapOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeMapOptionalTest.java new file mode 100644 index 0000000000..a8adbcb6f8 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeMapOptionalTest.java @@ -0,0 +1,89 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeMapOptionalTest extends RxJavaTest { + + @Test + public void successSuccess() { + Maybe.just(1) + .mapOptional(Optional::of) + .test() + .assertResult(1); + } + + @Test + public void successEmpty() { + Maybe.just(1) + .mapOptional(v -> Optional.empty()) + .test() + .assertResult(); + } + + @Test + public void empty() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Maybe.empty() + .mapOptional(f) + .test() + .assertResult(); + + verify(f, never()).apply(any()); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Maybe.error(new TestException()) + .mapOptional(f) + .test() + .assertFailure(TestException.class); + + verify(f, never()).apply(any()); + } + + @Test + public void mapperCrash() { + Maybe.just(1) + .mapOptional(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Maybe.never().mapOptional(Optional::of)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(m -> m.mapOptional(Optional::of)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeToCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeToCompletionStageTest.java new file mode 100644 index 0000000000..12e4ca5f1e --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/MaybeToCompletionStageTest.java @@ -0,0 +1,174 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeToCompletionStageTest extends RxJavaTest { + + @Test + public void just() throws Exception { + Integer v = Maybe.just(1) + .toCompletionStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void empty() throws Exception { + Integer v = Maybe.empty() + .toCompletionStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void emptyError() throws Exception { + CompletableFuture cf = Maybe.empty() + .toCompletionStage() + .toCompletableFuture(); + + TestHelper.assertError(cf, NoSuchElementException.class); + } + + @Test + public void completableFutureCancels() throws Exception { + MaybeSubject source = MaybeSubject.create(); + + CompletableFuture cf = source + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void completableManualCompleteCancels() throws Exception { + MaybeSubject source = MaybeSubject.create(); + + CompletableFuture cf = source + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void completableManualCompleteExceptionallyCancels() throws Exception { + MaybeSubject source = MaybeSubject.create(); + + CompletableFuture cf = source + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void error() throws Exception { + CompletableFuture cf = Maybe.error(new TestException()) + .toCompletionStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void sourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Maybe() { + @Override + protected void subscribeActual(MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .toCompletionStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void doubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Maybe() { + @Override + protected void subscribeActual(MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + } + } + .toCompletionStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableBlockingStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableBlockingStreamTest.java new file mode 100644 index 0000000000..275b38a57b --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableBlockingStreamTest.java @@ -0,0 +1,107 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.List; +import java.util.stream.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.processors.UnicastProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class ObservableBlockingStreamTest extends RxJavaTest { + + @Test + public void empty() { + try (Stream stream = Observable.empty().blockingStream()) { + assertEquals(0, stream.toArray().length); + } + } + + @Test + public void just() { + try (Stream stream = Observable.just(1).blockingStream()) { + assertArrayEquals(new Integer[] { 1 }, stream.toArray(Integer[]::new)); + } + } + + @Test + public void range() { + try (Stream stream = Observable.range(1, 5).blockingStream()) { + assertArrayEquals(new Integer[] { 1, 2, 3, 4, 5 }, stream.toArray(Integer[]::new)); + } + } + + @Test + public void rangeBackpressured() { + try (Stream stream = Observable.range(1, 5).blockingStream(1)) { + assertArrayEquals(new Integer[] { 1, 2, 3, 4, 5 }, stream.toArray(Integer[]::new)); + } + } + + @Test + public void rangeAsyncBackpressured() { + try (Stream stream = Observable.range(1, 1000).subscribeOn(Schedulers.computation()).blockingStream()) { + List list = stream.collect(Collectors.toList()); + + assertEquals(1000, list.size()); + for (int i = 1; i <= 1000; i++) { + assertEquals(i, list.get(i - 1).intValue()); + } + } + } + + @Test + public void rangeAsyncBackpressured1() { + try (Stream stream = Observable.range(1, 1000).subscribeOn(Schedulers.computation()).blockingStream(1)) { + List list = stream.collect(Collectors.toList()); + + assertEquals(1000, list.size()); + for (int i = 1; i <= 1000; i++) { + assertEquals(i, list.get(i - 1).intValue()); + } + } + } + + @Test + public void error() { + try (Stream stream = Observable.error(new TestException()).blockingStream()) { + stream.toArray(Integer[]::new); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + } + + @Test + public void close() { + UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); + up.onNext(2); + up.onNext(3); + up.onNext(4); + up.onNext(5); + + try (Stream stream = up.blockingStream()) { + assertArrayEquals(new Integer[] { 1, 2, 3 }, stream.limit(3).toArray(Integer[]::new)); + } + + assertFalse(up.hasSubscribers()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorTest.java new file mode 100644 index 0000000000..f18f42a3d0 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableCollectWithCollectorTest.java @@ -0,0 +1,444 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.assertFalse; + +import java.io.IOException; +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableCollectWithCollectorTest extends RxJavaTest { + + @Test + public void basic() { + Observable.range(1, 5) + .collect(Collectors.toList()) + .test() + .assertResult(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void empty() { + Observable.empty() + .collect(Collectors.toList()) + .test() + .assertResult(Collections.emptyList()); + } + + @Test + public void error() { + Observable.error(new TestException()) + .collect(Collectors.toList()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorSupplierCrash() { + Observable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + throw new TestException(); + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorCrash() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void collectorFinisherCrash() { + Observable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> { throw new TestException(); }; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorDropSignals() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable source = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + observer.onError(new IOException()); + observer.onComplete(); + } + }; + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(PublishSubject.create() + .collect(Collectors.toList())); + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeObservableToSingle(f -> f.collect(Collectors.toList())); + } + + @Test + public void basicToObservable() { + Observable.range(1, 5) + .collect(Collectors.toList()) + .toObservable() + .test() + .assertResult(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void emptyToObservable() { + Observable.empty() + .collect(Collectors.toList()) + .toObservable() + .test() + .assertResult(Collections.emptyList()); + } + + @Test + public void errorToObservable() { + Observable.error(new TestException()) + .collect(Collectors.toList()) + .toObservable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorSupplierCrashToObservable() { + Observable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + throw new TestException(); + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toObservable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorCrashToObservable() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toObservable() + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + public void collectorFinisherCrashToObservable() { + Observable.range(1, 5) + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> { throw new TestException(); }; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toObservable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorDropSignalsToObservable() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable source = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + observer.onError(new IOException()); + observer.onComplete(); + } + }; + + source + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .toObservable() + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void disposeToObservable() { + TestHelper.checkDisposed(PublishProcessor.create() + .collect(Collectors.toList()).toObservable()); + } + + @Test + public void onSubscribeToObservable() { + TestHelper.checkDoubleOnSubscribeObservable(f -> f.collect(Collectors.toList()).toObservable()); + } + + @Test + public void toObservableTake() { + Observable.range(1, 5) + .collect(Collectors.toList()) + .toObservable() + .take(1) + .test() + .assertResult(Arrays.asList(1, 2, 3, 4, 5)); + } + + @Test + public void disposeBeforeEnd() { + TestObserver> to = Observable.range(1, 5).concatWith(Observable.never()) + .collect(Collectors.toList()) + .test(); + + to.dispose(); + + to.assertEmpty(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFlatMapStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFlatMapStreamTest.java new file mode 100644 index 0000000000..5afcfa33ff --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFlatMapStreamTest.java @@ -0,0 +1,466 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableFlatMapStreamTest extends RxJavaTest { + + @Test + public void empty() { + Observable.empty() + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertResult(); + } + + @Test + public void emptyHidden() { + Observable.empty() + .hide() + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertResult(); + } + + @Test + public void just() { + Observable.just(1) + .flatMapStream(v -> Stream.of(v + 1, v + 2, v + 3, v + 4, v + 5)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void justHidden() { + Observable.just(1).hide() + .flatMapStream(v -> Stream.of(v + 1, v + 2, v + 3, v + 4, v + 5)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void error() { + Observable.error(new TestException()) + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void supplierFusedError() { + Observable.fromCallable(() -> { throw new TestException(); }) + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void errorHidden() { + Observable.error(new TestException()) + .hide() + .flatMapStream(v -> Stream.of(1, 2, 3, 4, 5)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void range() { + Observable.range(1, 5) + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31, 32, 33, 34, + 40, 41, 42, 43, 44, + 50, 51, 52, 53, 54 + ); + } + + @Test + public void rangeHidden() { + Observable.range(1, 5) + .hide() + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31, 32, 33, 34, + 40, 41, 42, 43, 44, + 50, 51, 52, 53, 54 + ); + } + + @Test + public void rangeToEmpty() { + Observable.range(1, 5) + .flatMapStream(v -> Stream.of()) + .test() + .assertResult(); + } + + @Test + public void rangeTake() { + Observable.range(1, 5) + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .take(12) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31 + ); + } + + @Test + public void rangeTakeHidden() { + Observable.range(1, 5) + .hide() + .flatMapStream(v -> IntStream.range(v * 10, v * 10 + 5).boxed()) + .take(12) + .test() + .assertResult( + 10, 11, 12, 13, 14, + 20, 21, 22, 23, 24, + 30, 31 + ); + } + + @Test + public void upstreamCancelled() { + PublishSubject ps = PublishSubject.create(); + + AtomicInteger calls = new AtomicInteger(); + + TestObserver to = ps + .flatMapStream(v -> Stream.of(v + 1, v + 2).onClose(() -> calls.getAndIncrement())) + .take(1) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.assertResult(2); + + assertFalse(ps.hasObservers()); + + assertEquals(1, calls.get()); + } + + @Test + public void upstreamCancelledCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps + .flatMapStream(v -> Stream.of(v + 1, v + 2).onClose(() -> { throw new TestException(); })) + .take(1) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.assertResult(2); + + assertFalse(ps.hasObservers()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void crossMap() { + Observable.range(1, 1000) + .flatMapStream(v -> IntStream.range(v * 1000, v * 1000 + 1000).boxed()) + .test() + .assertValueCount(1_000_000) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void crossMapHidden() { + Observable.range(1, 1000) + .hide() + .flatMapStream(v -> IntStream.range(v * 1000, v * 1000 + 1000).boxed()) + .test() + .assertValueCount(1_000_000) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(f -> f.flatMapStream(v -> Stream.of(1, 2))); + } + + @Test + public void mapperThrows() { + Observable.just(1).hide() + .concatMapStream(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperNull() { + Observable.just(1).hide() + .concatMapStream(v -> null) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void streamNull() { + Observable.just(1).hide() + .concatMapStream(v -> Stream.of(1, null)) + .test() + .assertFailure(NullPointerException.class, 1); + } + + @Test + public void hasNextThrows() { + Observable.just(1).hide() + .concatMapStream(v -> Stream.generate(() -> { throw new TestException(); })) + .test() + .assertFailure(TestException.class); + } + + @Test + public void hasNextThrowsLater() { + AtomicInteger counter = new AtomicInteger(); + Observable.just(1).hide() + .concatMapStream(v -> Stream.generate(() -> { + if (counter.getAndIncrement() == 0) { + return 1; + } + throw new TestException(); + })) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void mapperThrowsWhenUpstreamErrors() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishSubject ps = PublishSubject.create(); + + AtomicInteger counter = new AtomicInteger(); + + TestObserver to = ps.hide() + .concatMapStream(v -> { + if (counter.getAndIncrement() == 0) { + return Stream.of(1, 2); + } + ps.onError(new IOException()); + throw new TestException(); + }) + .test(); + + ps.onNext(1); + ps.onNext(2); + + to + .assertFailure(IOException.class, 1, 2); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void cancelAfterIteratorNext() throws Exception { + TestObserver to = new TestObserver<>(); + + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + to.dispose(); + return 1; + } + }); + + Observable.just(1) + .hide() + .concatMapStream(v -> stream) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void cancelAfterIteratorHasNext() throws Exception { + TestObserver to = new TestObserver<>(); + + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + to.dispose(); + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Observable.just(1) + .hide() + .concatMapStream(v -> stream) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void asyncUpstreamFused() { + UnicastSubject us = UnicastSubject.create(); + + TestObserver to = us.flatMapStream(v -> Stream.of(1, 2)) + .test(); + + assertTrue(us.hasObservers()); + + us.onNext(1); + + to.assertValuesOnly(1, 2); + + us.onComplete(); + + to.assertResult(1, 2); + } + + @Test + public void asyncUpstreamFusionBoundary() { + UnicastSubject us = UnicastSubject.create(); + + TestObserver to = us + .map(v -> v + 1) + .flatMapStream(v -> Stream.of(1, 2)) + .test(); + + assertTrue(us.hasObservers()); + + us.onNext(1); + + to.assertValuesOnly(1, 2); + + us.onComplete(); + + to.assertResult(1, 2); + } + + @Test + public void fusedPollCrash() { + UnicastSubject us = UnicastSubject.create(); + + TestObserver to = us + .map(v -> { throw new TestException(); }) + .compose(TestHelper.observableStripBoundary()) + .flatMapStream(v -> Stream.of(1, 2)) + .test(); + + assertTrue(us.hasObservers()); + + us.onNext(1); + + assertFalse(us.hasObservers()); + + to.assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(PublishSubject.create().flatMapStream(v -> Stream.of(1))); + } + + @Test + public void eventsIgnoredAfterCrash() { + AtomicInteger calls = new AtomicInteger(); + + new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + observer.onComplete(); + } + } + .flatMapStream(v -> { + calls.getAndIncrement(); + throw new TestException(); + }) + .take(1) + .test() + .assertFailure(TestException.class); + + assertEquals(1, calls.get()); + } + + @Test + public void eventsIgnoredAfterDispose() { + AtomicInteger calls = new AtomicInteger(); + + new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + observer.onComplete(); + } + } + .flatMapStream(v -> { + calls.getAndIncrement(); + return Stream.of(1); + }) + .take(1) + .test() + .assertResult(1); + + assertEquals(1, calls.get()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromCompletionStageTest.java new file mode 100644 index 0000000000..2be5ecf510 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromCompletionStageTest.java @@ -0,0 +1,65 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; + +public class ObservableFromCompletionStageTest extends RxJavaTest { + + @Test + public void syncSuccess() { + Observable.fromCompletionStage(CompletableFuture.completedFuture(1)) + .test() + .assertResult(1); + } + + @Test + public void syncFailure() { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new TestException()); + + Observable.fromCompletionStage(cf) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncNull() { + Observable.fromCompletionStage(CompletableFuture.completedFuture(null)) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void cancel() { + CompletableFuture cf = new CompletableFuture<>(); + + TestObserver to = Observable.fromCompletionStage(cf) + .test(); + + to.assertEmpty(); + + to.dispose(); + + cf.complete(1); + + to.assertEmpty(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromOptionalTest.java new file mode 100644 index 0000000000..5fbeb1f2ff --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromOptionalTest.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.Optional; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; + +public class ObservableFromOptionalTest extends RxJavaTest { + + @Test + public void hasValue() { + Observable.fromOptional(Optional.of(1)) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Observable.fromOptional(Optional.empty()) + .test() + .assertResult(); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromStreamTest.java new file mode 100644 index 0000000000..2b943e5338 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableFromStreamTest.java @@ -0,0 +1,490 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.Iterator; +import java.util.concurrent.atomic.*; +import java.util.stream.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.SimpleQueue; +import io.reactivex.rxjava3.testsupport.*; + +public class ObservableFromStreamTest extends RxJavaTest { + + @Test + public void empty() { + Observable.fromStream(Stream.of()) + .test() + .assertResult(); + } + + @Test + public void just() { + Observable.fromStream(Stream.of(1)) + .test() + .assertResult(1); + } + + @Test + public void many() { + Observable.fromStream(Stream.of(1, 2, 3, 4, 5)) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void noReuse() { + Observable source = Observable.fromStream(Stream.of(1, 2, 3, 4, 5)); + + source + .test() + .assertResult(1, 2, 3, 4, 5); + + source + .test() + .assertFailure(IllegalStateException.class); + } + + @Test + public void take() { + Observable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .take(5) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void emptyConditional() { + Observable.fromStream(Stream.of()) + .filter(v -> true) + .test() + .assertResult(); + } + + @Test + public void justConditional() { + Observable.fromStream(Stream.of(1)) + .filter(v -> true) + .test() + .assertResult(1); + } + + @Test + public void manyConditional() { + Observable.fromStream(Stream.of(1, 2, 3, 4, 5)) + .filter(v -> true) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void manyConditionalSkip() { + Observable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .filter(v -> v % 2 == 0) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void takeConditional() { + Observable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .filter(v -> true) + .take(5) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void noOfferNoCrashAfterClear() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + + Observable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + queue.set((SimpleQueue)d); + ((QueueDisposable)d).requestFusion(QueueFuseable.ANY); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + TestHelper.assertNoOffer(q); + + assertFalse(q.isEmpty()); + + q.clear(); + + assertNull(q.poll()); + + assertTrue(q.isEmpty()); + + q.clear(); + + assertNull(q.poll()); + + assertTrue(q.isEmpty()); + } + + @Test + public void fusedPoll() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of(1).onClose(() -> calls.getAndIncrement())) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + queue.set((SimpleQueue)d); + ((QueueDisposable)d).requestFusion(QueueFuseable.ANY); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + + assertFalse(q.isEmpty()); + + assertEquals(1, q.poll()); + + assertTrue(q.isEmpty()); + + assertEquals(1, calls.get()); + } + + @Test + public void fusedPoll2() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of(1, 2).onClose(() -> calls.getAndIncrement())) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + queue.set((SimpleQueue)d); + ((QueueDisposable)d).requestFusion(QueueFuseable.ANY); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + + assertFalse(q.isEmpty()); + + assertEquals(1, q.poll()); + + assertFalse(q.isEmpty()); + + assertEquals(2, q.poll()); + + assertTrue(q.isEmpty()); + + assertEquals(1, calls.get()); + } + + @Test + public void streamOfNull() { + Observable.fromStream(Stream.of((Integer)null)) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void streamOfNullConditional() { + Observable.fromStream(Stream.of((Integer)null)) + .filter(v -> true) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void syncFusionSupport() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Observable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .subscribeWith(to) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void asyncFusionNotSupported() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ASYNC); + + Observable.fromStream(IntStream.rangeClosed(1, 10).boxed()) + .subscribeWith(to) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void runToEndCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Stream stream = Stream.of(1, 2, 3, 4, 5).onClose(() -> { throw new TestException(); }); + + Observable.fromStream(stream) + .test() + .assertResult(1, 2, 3, 4, 5); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void takeCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Stream stream = Stream.of(1, 2, 3, 4, 5).onClose(() -> { throw new TestException(); }); + + Observable.fromStream(stream) + .take(3) + .test() + .assertResult(1, 2, 3); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void hasNextCrash() { + AtomicInteger v = new AtomicInteger(); + Observable.fromStream(Stream.generate(() -> { + int value = v.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + })) + .test() + .assertFailure(TestException.class, 0); + } + + @Test + public void hasNextCrashConditional() { + AtomicInteger counter = new AtomicInteger(); + Observable.fromStream(Stream.generate(() -> { + int value = counter.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + })) + .filter(v -> true) + .test() + .assertFailure(TestException.class, 0); + } + + @Test + public void closeCalledOnEmpty() { + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of().onClose(() -> calls.getAndIncrement())) + .test() + .assertResult(); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledAfterItems() { + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .test() + .assertResult(1, 2, 3, 4, 5); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnCancel() { + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .take(3) + .test() + .assertResult(1, 2, 3); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnItemCrash() { + AtomicInteger calls = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + Observable.fromStream(Stream.generate(() -> { + int value = counter.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + }).onClose(() -> calls.getAndIncrement())) + .test() + .assertFailure(TestException.class, 0); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledAfterItemsConditional() { + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .filter(v -> true) + .test() + .assertResult(1, 2, 3, 4, 5); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnCancelConditional() { + AtomicInteger calls = new AtomicInteger(); + + Observable.fromStream(Stream.of(1, 2, 3, 4, 5).onClose(() -> calls.getAndIncrement())) + .filter(v -> true) + .take(3) + .test() + .assertResult(1, 2, 3); + + assertEquals(1, calls.get()); + } + + @Test + public void closeCalledOnItemCrashConditional() { + AtomicInteger calls = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + Observable.fromStream(Stream.generate(() -> { + int value = counter.getAndIncrement(); + if (value == 1) { + throw new TestException(); + } + return value; + }).onClose(() -> calls.getAndIncrement())) + .filter(v -> true) + .test() + .assertFailure(TestException.class, 0); + + assertEquals(1, calls.get()); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Observable.fromStream(Stream.of(1))); + } + + @Test + public void cancelAfterIteratorNext() throws Exception { + TestObserver to = new TestObserver<>(); + + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + to.dispose(); + return 1; + } + }); + + Observable.fromStream(stream) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void cancelAfterIteratorHasNext() throws Exception { + TestObserver to = new TestObserver<>(); + + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + int calls; + + @Override + public boolean hasNext() { + if (++calls == 1) { + to.dispose(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Observable.fromStream(stream) + .subscribe(to); + + to.assertEmpty(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableMapOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableMapOptionalTest.java new file mode 100644 index 0000000000..1b0a7f0a7d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableMapOptionalTest.java @@ -0,0 +1,394 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.assertFalse; + +import java.util.Optional; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableMapOptionalTest extends RxJavaTest { + + static final Function> MODULO = v -> v % 2 == 0 ? Optional.of(v) : Optional.empty(); + + @Test + public void allPresent() { + Observable.range(1, 5) + .mapOptional(Optional::of) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void allEmpty() { + Observable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .test() + .assertResult(); + } + + @Test + public void mixed() { + Observable.range(1, 10) + .mapOptional(MODULO) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void mapperChash() { + BehaviorSubject source = BehaviorSubject.createDefault(1); + + source + .mapOptional(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasObservers()); + } + + @Test + public void mapperNull() { + BehaviorSubject source = BehaviorSubject.createDefault(1); + + source + .mapOptional(v -> null) + .test() + .assertFailure(NullPointerException.class); + + assertFalse(source.hasObservers()); + } + + @Test + public void crashDropsOnNexts() { + Observable source = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + } + }; + + source + .mapOptional(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncFusedAll() { + Observable.range(1, 5) + .mapOptional(Optional::of) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void asyncFusedAll() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(Optional::of) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void boundaryFusedAll() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(Optional::of) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void syncFusedNone() { + Observable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(); + } + + @Test + public void asyncFusedNone() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(v -> Optional.empty()) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void boundaryFusedNone() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(v -> Optional.empty()) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(); + } + + @Test + public void syncFusedMixed() { + Observable.range(1, 10) + .mapOptional(MODULO) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void asyncFusedMixed() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + us + .mapOptional(MODULO) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void boundaryFusedMixed() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + us + .mapOptional(MODULO) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void allPresentConditional() { + Observable.range(1, 5) + .mapOptional(Optional::of) + .filter(v -> true) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void allEmptyConditional() { + Observable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .test() + .assertResult(); + } + + @Test + public void mixedConditional() { + Observable.range(1, 10) + .mapOptional(MODULO) + .filter(v -> true) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void mapperChashConditional() { + BehaviorSubject source = BehaviorSubject.createDefault(1); + + source + .mapOptional(v -> { throw new TestException(); }) + .filter(v -> true) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasObservers()); + } + + @Test + public void mapperNullConditional() { + BehaviorSubject source = BehaviorSubject.createDefault(1); + + source + .mapOptional(v -> null) + .filter(v -> true) + .test() + .assertFailure(NullPointerException.class); + + assertFalse(source.hasObservers()); + } + + @Test + public void crashDropsOnNextsConditional() { + Observable source = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + } + }; + + source + .mapOptional(v -> { throw new TestException(); }) + .filter(v -> true) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncFusedAllConditional() { + Observable.range(1, 5) + .mapOptional(Optional::of) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void asyncFusedAllConditional() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(Optional::of) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void boundaryFusedAllConditiona() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(Optional::of) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void syncFusedNoneConditional() { + Observable.range(1, 5) + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(); + } + + @Test + public void asyncFusedNoneConditional() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void boundaryFusedNoneConditional() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(); + } + + @Test + public void syncFusedMixedConditional() { + Observable.range(1, 10) + .mapOptional(MODULO) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.SYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void asyncFusedMixedConditional() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + us + .mapOptional(MODULO) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void boundaryFusedMixedConditional() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + + us + .mapOptional(MODULO) + .filter(v -> true) + .to(TestHelper.testConsumer(false, QueueFuseable.ASYNC | QueueFuseable.BOUNDARY)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(2, 4, 6, 8, 10); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageSubscriberOrDefaultTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageSubscriberOrDefaultTest.java new file mode 100644 index 0000000000..f652c9b327 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageSubscriberOrDefaultTest.java @@ -0,0 +1,475 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableStageSubscriberOrDefaultTest extends RxJavaTest { + + @Test + public void firstJust() throws Exception { + Integer v = Observable.just(1) + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void firstEmpty() throws Exception { + Integer v = Observable.empty() + .firstStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void firstCancels() throws Exception { + BehaviorSubject source = BehaviorSubject.createDefault(1); + + Integer v = source + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + assertFalse(source.hasObservers()); + } + + @Test + public void firstCompletableFutureCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .firstStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void firstCompletableManualCompleteCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .firstStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void firstCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .firstStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstError() throws Exception { + CompletableFuture cf = Observable.error(new TestException()) + .firstStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void firstDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + } + } + .firstStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void singleJust() throws Exception { + Integer v = Observable.just(1) + .singleStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void singleEmpty() throws Exception { + Integer v = Observable.empty() + .singleStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void singleTooManyCancels() throws Exception { + ReplaySubject source = ReplaySubject.create(); + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(source + .singleStage(null) + .toCompletableFuture(), IllegalArgumentException.class); + + assertFalse(source.hasObservers()); + } + + @Test + public void singleCompletableFutureCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .singleStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void singleCompletableManualCompleteCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .singleStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void singleCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .singleStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleError() throws Exception { + CompletableFuture cf = Observable.error(new TestException()) + .singleStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .singleStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void singleDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + } + } + .singleStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void lastJust() throws Exception { + Integer v = Observable.just(1) + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void lastRange() throws Exception { + Integer v = Observable.range(1, 5) + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)5, v); + } + + @Test + public void lastEmpty() throws Exception { + Integer v = Observable.empty() + .lastStage(2) + .toCompletableFuture() + .get(); + + assertEquals((Integer)2, v); + } + + @Test + public void lastCompletableFutureCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .lastStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void lastCompletableManualCompleteCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .lastStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void lastCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .lastStage(null) + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastError() throws Exception { + CompletableFuture cf = Observable.error(new TestException()) + .lastStage(null) + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void lastDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + } + } + .lastStage(null) + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageSubscriberOrErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageSubscriberOrErrorTest.java new file mode 100644 index 0000000000..5646fc3d59 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ObservableStageSubscriberOrErrorTest.java @@ -0,0 +1,469 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableStageSubscriberOrErrorTest extends RxJavaTest { + + @Test + public void firstJust() throws Exception { + Integer v = Observable.just(1) + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void firstEmpty() throws Exception { + TestHelper.assertError( + Observable.empty() + .firstOrErrorStage() + .toCompletableFuture(), NoSuchElementException.class); + } + + @Test + public void firstCancels() throws Exception { + BehaviorSubject source = BehaviorSubject.createDefault(1); + + Integer v = source + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + assertFalse(source.hasObservers()); + } + + @Test + public void firstCompletableFutureCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void firstCompletableManualCompleteCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void firstCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstError() throws Exception { + CompletableFuture cf = Observable.error(new TestException()) + .firstOrErrorStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void firstSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void firstDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + } + } + .firstOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void singleJust() throws Exception { + Integer v = Observable.just(1) + .singleOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void singleEmpty() throws Exception { + TestHelper.assertError( + Observable.empty() + .singleOrErrorStage() + .toCompletableFuture(), NoSuchElementException.class); + } + + @Test + public void singleTooManyCancels() throws Exception { + ReplaySubject source = ReplaySubject.create(); + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(source + .singleOrErrorStage() + .toCompletableFuture(), IllegalArgumentException.class); + + assertFalse(source.hasObservers()); + } + + @Test + public void singleCompletableFutureCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void singleCompletableManualCompleteCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void singleCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleError() throws Exception { + CompletableFuture cf = Observable.error(new TestException()) + .singleOrErrorStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void singleSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .singleOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void singleDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + } + } + .singleOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } + + @Test + public void lastJust() throws Exception { + Integer v = Observable.just(1) + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void lastRange() throws Exception { + Integer v = Observable.range(1, 5) + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)5, v); + } + + @Test + public void lastEmpty() throws Exception { + TestHelper.assertError(Observable.empty() + .lastOrErrorStage() + .toCompletableFuture(), NoSuchElementException.class); + } + + @Test + public void lastCompletableFutureCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void lastCompletableManualCompleteCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void lastCompletableManualCompleteExceptionallyCancels() throws Exception { + PublishSubject source = PublishSubject.create(); + + CompletableFuture cf = source + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastError() throws Exception { + CompletableFuture cf = Observable.error(new TestException()) + .lastOrErrorStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void lastSourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + observer.onError(new TestException()); + observer.onComplete(); + } + } + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void lastDoubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Observable() { + @Override + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + } + } + .lastOrErrorStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java new file mode 100644 index 0000000000..9d0fc09f62 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelCollectorTest.java @@ -0,0 +1,306 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.*; +import java.util.stream.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.parallel.ParallelInvalid; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.processors.BehaviorProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class ParallelCollectorTest extends RxJavaTest { + + static Set set(int count) { + return IntStream.rangeClosed(1, count) + .boxed() + .collect(Collectors.toSet()); + } + + @Test + public void basic() { + TestSubscriberEx> ts = Flowable.range(1, 5) + .parallel() + .collect(Collectors.toList()) + .subscribeWith(new TestSubscriberEx<>()); + + ts + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + assertEquals(5, ts.values().get(0).size()); + assertTrue(ts.values().get(0).containsAll(set(5))); + } + + @Test + public void empty() { + Flowable.empty() + .parallel() + .collect(Collectors.toList()) + .test() + .assertResult(Collections.emptyList()); + } + + @Test + public void error() { + Flowable.error(new TestException()) + .parallel() + .collect(Collectors.toList()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorSupplierCrash() { + Flowable.range(1, 5) + .parallel() + .collect(new Collector() { + + @Override + public Supplier supplier() { + throw new TestException(); + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorAccumulatorCrash() { + BehaviorProcessor source = BehaviorProcessor.createDefault(1); + + source + .parallel() + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + } + + @Test + @SuppressUndeliverable + public void collectorCombinerCrash() { + Flowable.range(1, 5) + .parallel() + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> { throw new TestException(); }; + } + + @Override + public Function finisher() { + return a -> a; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void collectorFinisherCrash() { + Flowable.range(1, 5) + .parallel() + .collect(new Collector() { + + @Override + public Supplier supplier() { + return () -> 1; + } + + @Override + public BiConsumer accumulator() { + return (a, b) -> { }; + } + + @Override + public BinaryOperator combiner() { + return (a, b) -> a + b; + } + + @Override + public Function finisher() { + return a -> { throw new TestException(); }; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void async() { + for (int i = 1; i < 32; i++) { + TestSubscriber> ts = Flowable.range(1, 1000) + .parallel(i) + .runOn(Schedulers.computation()) + .collect(Collectors.toList()) + .test() + .withTag("Parallelism: " + i) + .awaitDone(5, TimeUnit.SECONDS) + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + assertEquals(1000, ts.values().get(0).size()); + + assertTrue(ts.values().get(0).containsAll(set(1000))); + } + } + + @Test + public void asyncHidden() { + for (int i = 1; i < 32; i++) { + TestSubscriber> ts = Flowable.range(1, 1000) + .hide() + .parallel(i) + .runOn(Schedulers.computation()) + .collect(Collectors.toList()) + .test() + .withTag("Parallelism: " + i) + .awaitDone(5, TimeUnit.SECONDS) + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + assertEquals(1000, ts.values().get(0).size()); + + assertTrue(ts.values().get(0).containsAll(set(1000))); + } + } + + @Test + public void doubleError() { + List errors = TestHelper.trackPluginErrors(); + try { + new ParallelInvalid() + .collect(Collectors.toList()) + .test() + .assertFailure(TestException.class); + + assertFalse(errors.isEmpty()); + for (Throwable ex : errors) { + assertTrue(ex.toString(), ex.getCause() instanceof TestException); + } + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void asyncSum() { + long n = 1_000; + for (int i = 1; i < 32; i++) { + Flowable.rangeLong(1, n) + .parallel(i) + .runOn(Schedulers.computation()) + .collect(Collectors.summingLong(v -> v)) + .test() + .withTag("Parallelism: " + i) + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(n * (n + 1) / 2); + } + } + + @Test + public void asyncSumLong() { + long n = 1_000_000; + Flowable.rangeLong(1, n) + .parallel() + .runOn(Schedulers.computation()) + .collect(Collectors.summingLong(v -> v)) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(n * (n + 1) / 2); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelFlatMapStreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelFlatMapStreamTest.java new file mode 100644 index 0000000000..f1d34491d5 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelFlatMapStreamTest.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.Stream; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.parallel.ParallelFlowableTest; + +public class ParallelFlatMapStreamTest extends RxJavaTest { + + @Test + public void subscriberCount() { + ParallelFlowableTest.checkSubscriberCount(Flowable.range(1, 5).parallel() + .flatMapStream(v -> Stream.of(1, 2, 3))); + } + + @Test + public void normal() { + for (int i = 1; i < 32; i++) { + Flowable.range(1, 1000) + .parallel(i) + .flatMapStream(v -> Stream.of(v, v + 1)) + .sequential() + .test() + .withTag("Parallelism: " + i) + .assertValueCount(2000) + .assertNoErrors() + .assertComplete(); + } + } + + @Test + public void none() { + for (int i = 1; i < 32; i++) { + Flowable.range(1, 1000) + .parallel(i) + .flatMapStream(v -> Stream.of()) + .sequential() + .test() + .withTag("Parallelism: " + i) + .assertResult(); + } + } + + @Test + public void mixed() { + for (int i = 1; i < 32; i++) { + Flowable.range(1, 1000) + .parallel(i) + .flatMapStream(v -> v % 2 == 0 ? Stream.of(v) : Stream.of()) + .sequential() + .test() + .withTag("Parallelism: " + i) + .assertValueCount(500) + .assertNoErrors() + .assertComplete(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapOptionalTest.java new file mode 100644 index 0000000000..ca7385b7f6 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapOptionalTest.java @@ -0,0 +1,228 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.parallel.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ParallelMapOptionalTest extends RxJavaTest { + + @Test + public void doubleFilter() { + Flowable.range(1, 10) + .parallel() + .mapOptional(Optional::of) + .filter(new Predicate() { + @Override + public boolean test(Integer v) throws Exception { + return v % 2 == 0; + } + }) + .filter(new Predicate() { + @Override + public boolean test(Integer v) throws Exception { + return v % 3 == 0; + } + }) + .sequential() + .test() + .assertResult(6); + } + + @Test + public void doubleFilterAsync() { + Flowable.range(1, 10) + .parallel() + .runOn(Schedulers.computation()) + .mapOptional(Optional::of) + .filter(new Predicate() { + @Override + public boolean test(Integer v) throws Exception { + return v % 2 == 0; + } + }) + .filter(new Predicate() { + @Override + public boolean test(Integer v) throws Exception { + return v % 3 == 0; + } + }) + .sequential() + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(6); + } + + @Test + public void doubleError() { + List errors = TestHelper.trackPluginErrors(); + try { + new ParallelInvalid() + .mapOptional(Optional::of) + .sequential() + .test() + .assertFailure(TestException.class); + + assertFalse(errors.isEmpty()); + for (Throwable ex : errors) { + assertTrue(ex.toString(), ex.getCause() instanceof TestException); + } + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void doubleError2() { + List errors = TestHelper.trackPluginErrors(); + try { + new ParallelInvalid() + .mapOptional(Optional::of) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertFailure(TestException.class); + + assertFalse(errors.isEmpty()); + for (Throwable ex : errors) { + assertTrue(ex.toString(), ex.getCause() instanceof TestException); + } + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void error() { + Flowable.error(new TestException()) + .parallel() + .mapOptional(Optional::of) + .sequential() + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapCrash() { + Flowable.just(1) + .parallel() + .mapOptional(v -> { throw new TestException(); }) + .sequential() + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapCrashConditional() { + Flowable.just(1) + .parallel() + .mapOptional(v -> { throw new TestException(); }) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapCrashConditional2() { + Flowable.just(1) + .parallel() + .runOn(Schedulers.computation()) + .mapOptional(v -> { throw new TestException(); }) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertFailure(TestException.class); + } + + @Test + public void allNone() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> Optional.empty()) + .sequential() + .test() + .assertResult(); + } + + @Test + public void allNoneConditional() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> Optional.empty()) + .filter(v -> true) + .sequential() + .test() + .assertResult(); + } + + @Test + public void mixed() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> v % 2 == 0 ? Optional.of(v) : Optional.empty()) + .sequential() + .test() + .assertValueCount(500) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void mixedConditional() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> v % 2 == 0 ? Optional.of(v) : Optional.empty()) + .filter(v -> true) + .sequential() + .test() + .assertValueCount(500) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void invalidSubscriberCount() { + TestHelper.checkInvalidParallelSubscribers( + Flowable.range(1, 10).parallel() + .mapOptional(Optional::of) + ); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.mapOptional(Optional::of) + ); + + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.mapOptional(Optional::of) + .filter(v -> true) + ); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapTryOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapTryOptionalTest.java new file mode 100644 index 0000000000..459b3f884a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/ParallelMapTryOptionalTest.java @@ -0,0 +1,378 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.parallel.*; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.testsupport.*; + +public class ParallelMapTryOptionalTest extends RxJavaTest implements Consumer { + + volatile int calls; + + @Override + public void accept(Object t) throws Exception { + calls++; + } + + @Test + public void mapNoError() { + for (ParallelFailureHandling e : ParallelFailureHandling.values()) { + Flowable.just(1) + .parallel(1) + .mapOptional(Optional::of, e) + .sequential() + .test() + .assertResult(1); + } + } + + @Test + public void mapErrorNoError() { + for (ParallelFailureHandling e : ParallelFailureHandling.values()) { + Flowable.error(new TestException()) + .parallel(1) + .mapOptional(Optional::of, e) + .sequential() + .test() + .assertFailure(TestException.class); + } + } + + @Test + public void mapConditionalNoError() { + for (ParallelFailureHandling e : ParallelFailureHandling.values()) { + Flowable.just(1) + .parallel(1) + .mapOptional(Optional::of, e) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertResult(1); + } + } + + @Test + public void mapErrorConditionalNoError() { + for (ParallelFailureHandling e : ParallelFailureHandling.values()) { + Flowable.error(new TestException()) + .parallel(1) + .mapOptional(Optional::of, e) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertFailure(TestException.class); + } + } + + @Test + public void mapFailWithError() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), ParallelFailureHandling.ERROR) + .sequential() + .test() + .assertFailure(ArithmeticException.class); + } + + @Test + public void mapFailWithStop() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), ParallelFailureHandling.STOP) + .sequential() + .test() + .assertResult(); + } + + @Test + public void mapFailWithRetry() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(new Function>() { + int count; + @Override + public Optional apply(Integer v) throws Exception { + if (count++ == 1) { + return Optional.of(-1); + } + return Optional.of(1 / v); + } + }, ParallelFailureHandling.RETRY) + .sequential() + .test() + .assertResult(-1, 1); + } + + @Test + public void mapFailWithRetryLimited() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), new BiFunction() { + @Override + public ParallelFailureHandling apply(Long n, Throwable e) throws Exception { + return n < 5 ? ParallelFailureHandling.RETRY : ParallelFailureHandling.SKIP; + } + }) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void mapFailWithSkip() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void mapFailHandlerThrows() { + TestSubscriberEx ts = Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), new BiFunction() { + @Override + public ParallelFailureHandling apply(Long n, Throwable e) throws Exception { + throw new TestException(); + } + }) + .sequential() + .to(TestHelper.testConsumer()) + .assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, ArithmeticException.class, TestException.class); + } + + @Test + public void mapInvalidSource() { + List errors = TestHelper.trackPluginErrors(); + try { + new ParallelInvalid() + .mapOptional(Optional::of, ParallelFailureHandling.ERROR) + .sequential() + .test(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void mapFailWithErrorConditional() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), ParallelFailureHandling.ERROR) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertFailure(ArithmeticException.class); + } + + @Test + public void mapFailWithStopConditional() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), ParallelFailureHandling.STOP) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertResult(); + } + + @Test + public void mapFailWithRetryConditional() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(new Function>() { + int count; + @Override + public Optional apply(Integer v) throws Exception { + if (count++ == 1) { + return Optional.of(-1); + } + return Optional.of(1 / v); + } + }, ParallelFailureHandling.RETRY) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertResult(-1, 1); + } + + @Test + public void mapFailWithRetryLimitedConditional() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), new BiFunction() { + @Override + public ParallelFailureHandling apply(Long n, Throwable e) throws Exception { + return n < 5 ? ParallelFailureHandling.RETRY : ParallelFailureHandling.SKIP; + } + }) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void mapFailWithSkipConditional() { + Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), ParallelFailureHandling.SKIP) + .filter(Functions.alwaysTrue()) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void mapFailHandlerThrowsConditional() { + TestSubscriberEx ts = Flowable.range(0, 2) + .parallel(1) + .mapOptional(v -> Optional.of(1 / v), new BiFunction() { + @Override + public ParallelFailureHandling apply(Long n, Throwable e) throws Exception { + throw new TestException(); + } + }) + .filter(Functions.alwaysTrue()) + .sequential() + .to(TestHelper.testConsumer()) + .assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, ArithmeticException.class, TestException.class); + } + + @Test + public void mapWrongParallelismConditional() { + TestHelper.checkInvalidParallelSubscribers( + Flowable.just(1).parallel(1) + .mapOptional(Optional::of, ParallelFailureHandling.ERROR) + .filter(Functions.alwaysTrue()) + ); + } + + @Test + public void mapInvalidSourceConditional() { + List errors = TestHelper.trackPluginErrors(); + try { + new ParallelInvalid() + .mapOptional(Optional::of, ParallelFailureHandling.ERROR) + .filter(Functions.alwaysTrue()) + .sequential() + .test(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void failureHandlingEnum() { + TestHelper.checkEnum(ParallelFailureHandling.class); + } + + @Test + public void allNone() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> Optional.empty(), ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertResult(); + } + + @Test + public void allNoneConditional() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> Optional.empty(), ParallelFailureHandling.SKIP) + .filter(v -> true) + .sequential() + .test() + .assertResult(); + } + + @Test + public void mixed() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> v % 2 == 0 ? Optional.of(v) : Optional.empty(), ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertValueCount(500) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void mixedConditional() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> v % 2 == 0 ? Optional.of(v) : Optional.empty(), ParallelFailureHandling.SKIP) + .filter(v -> true) + .sequential() + .test() + .assertValueCount(500) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void mixedConditional2() { + Flowable.range(1, 1000) + .parallel() + .mapOptional(v -> v % 2 == 0 ? Optional.of(v) : Optional.empty(), ParallelFailureHandling.SKIP) + .filter(v -> v % 4 == 0) + .sequential() + .test() + .assertValueCount(250) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void invalidSubscriberCount() { + TestHelper.checkInvalidParallelSubscribers( + Flowable.range(1, 10).parallel() + .mapOptional(Optional::of, ParallelFailureHandling.SKIP) + ); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.mapOptional(Optional::of, ParallelFailureHandling.ERROR) + ); + + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.mapOptional(Optional::of, ParallelFailureHandling.ERROR) + .filter(v -> true) + ); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowableTckTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowableTckTest.java new file mode 100644 index 0000000000..53c4206510 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowableTckTest.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.stream.*; + +import org.reactivestreams.Publisher; +import org.testng.annotations.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.tck.BaseTck; + +@Test +public class SingleFlattenStreamAsFlowableTckTest extends BaseTck { + + @Override + public Publisher createPublisher(final long elements) { + return + Single.just(1).flattenStreamAsFlowable(v -> IntStream.range(0, (int)elements).boxed()) + ; + } + + @Override + public Publisher createFailedPublisher() { + Stream stream = Stream.of(1); + stream.forEach(v -> { }); + return Single.just(1).flattenStreamAsFlowable(v -> stream); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowableTest.java new file mode 100644 index 0000000000..6e74b42370 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsFlowableTest.java @@ -0,0 +1,441 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.*; + +import org.junit.Test; +import org.reactivestreams.Subscription; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class SingleFlattenStreamAsFlowableTest extends RxJavaTest { + + @Test + public void successJust() { + Single.just(1) + .flattenStreamAsFlowable(Stream::of) + .test() + .assertResult(1); + } + + @Test + public void successEmpty() { + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of()) + .test() + .assertResult(); + } + + @Test + public void successMany() { + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(2, 3, 4, 5, 6)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void successManyTake() { + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(2, 3, 4, 5, 6)) + .take(3) + .test() + .assertResult(2, 3, 4); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Single.error(new TestException()) + .flattenStreamAsFlowable(f) + .test() + .assertFailure(TestException.class); + + verify(f, never()).apply(any()); + } + + @Test + public void mapperCrash() { + Single.just(1) + .flattenStreamAsFlowable(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.never().flattenStreamAsFlowable(Stream::of)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToFlowable(m -> m.flattenStreamAsFlowable(Stream::of)); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(SingleSubject.create().flattenStreamAsFlowable(Stream::of)); + } + + @Test + public void fusedEmpty() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of()) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void fusedJust() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void fusedMany() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3); + } + + @Test + public void fusedManyRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3); + } + + @Test + public void manyBackpressured() { + Single.just(1) + .flattenStreamAsFlowable(v -> IntStream.rangeClosed(1, 5).boxed()) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(1, 2) + .requestMore(2) + .assertValuesOnly(1, 2, 3, 4) + .requestMore(1) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void manyBackpressured2() { + Single.just(1) + .flattenStreamAsFlowable(v -> IntStream.rangeClosed(1, 5).boxed()) + .rebatchRequests(1) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(1, 2) + .requestMore(2) + .assertValuesOnly(1, 2, 3, 4) + .requestMore(1) + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void fusedStreamAvailableLater() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + SingleSubject ss = SingleSubject.create(); + + ss + .flattenStreamAsFlowable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertEmpty(); + + ss.onSuccess(1); + + ts + .assertResult(1, 2, 3); + } + + @Test + public void fused() throws Throwable { + AtomicReference> qsr = new AtomicReference<>(); + + SingleSubject ss = SingleSubject.create(); + + ss + .flattenStreamAsFlowable(Stream::of) + .subscribe(new FlowableSubscriber() { + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + @SuppressWarnings("unchecked") + public void onSubscribe(@NonNull Subscription s) { + qsr.set((QueueSubscription)s); + } + }); + + QueueSubscription qs = qsr.get(); + + assertEquals(QueueFuseable.ASYNC, qs.requestFusion(QueueFuseable.ASYNC)); + + assertTrue(qs.isEmpty()); + assertNull(qs.poll()); + + ss.onSuccess(1); + + assertFalse(qs.isEmpty()); + assertEquals(1, qs.poll().intValue()); + + assertTrue(qs.isEmpty()); + assertNull(qs.poll()); + + qs.cancel(); + + assertTrue(qs.isEmpty()); + assertNull(qs.poll()); + } + + @Test + public void requestOneByOne() { + TestSubscriber ts = new TestSubscriber<>(); + + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(1, 2, 3, 4, 5)) + .subscribe(new FlowableSubscriber() { + + Subscription upstream; + + @Override + public void onSubscribe(@NonNull Subscription s) { + ts.onSubscribe(new BooleanSubscription()); + upstream = s; + s.request(1); + } + + @Override + public void onNext(Integer t) { + ts.onNext(t); + upstream.request(1); + } + + @Override + public void onError(Throwable t) { + ts.onError(t); + } + + @Override + public void onComplete() { + ts.onComplete(); + } + }); + + ts.assertResult(1, 2, 3, 4, 5); + } + + @Test + public void streamCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.just(1) + .flattenStreamAsFlowable(v -> Stream.of(v).onClose(() -> { throw new TestException(); })) + .test() + .assertResult(1); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void hasNextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + throw new TestException(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Single.just(1) + .flattenStreamAsFlowable(v -> stream) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void nextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + throw new TestException(); + } + }); + + Single.just(1) + .flattenStreamAsFlowable(v -> stream) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelAfterHasNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestSubscriber ts = new TestSubscriber<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + ts.cancel(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Single.just(1) + .flattenStreamAsFlowable(v -> stream) + .subscribeWith(ts) + .assertValuesOnly(1); + } + + @Test + public void cancelAfterNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestSubscriber ts = new TestSubscriber<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + }); + + Single.just(1) + .flattenStreamAsFlowable(v -> stream) + .subscribeWith(ts) + .assertEmpty(); + } + + @Test + public void requestSuccessRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + SingleSubject ss = SingleSubject.create(); + + TestSubscriber ts = new TestSubscriber<>(0L); + + ss.flattenStreamAsFlowable(Stream::of) + .subscribe(ts); + + Runnable r1 = () -> ss.onSuccess(1); + Runnable r2 = () -> ts.request(1); + + TestHelper.race(r1, r2); + + ts.assertResult(1); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsObservableTest.java new file mode 100644 index 0000000000..2c50634823 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFlattenStreamAsObservableTest.java @@ -0,0 +1,419 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.*; + +public class SingleFlattenStreamAsObservableTest extends RxJavaTest { + + @Test + public void successJust() { + Single.just(1) + .flattenStreamAsObservable(Stream::of) + .test() + .assertResult(1); + } + + @Test + public void successEmpty() { + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of()) + .test() + .assertResult(); + } + + @Test + public void successMany() { + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of(2, 3, 4, 5, 6)) + .test() + .assertResult(2, 3, 4, 5, 6); + } + + @Test + public void successManyTake() { + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of(2, 3, 4, 5, 6)) + .take(3) + .test() + .assertResult(2, 3, 4); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Single.error(new TestException()) + .flattenStreamAsObservable(f) + .test() + .assertFailure(TestException.class); + + verify(f, never()).apply(any()); + } + + @Test + public void mapperCrash() { + Single.just(1) + .flattenStreamAsObservable(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.never().flattenStreamAsObservable(Stream::of)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToObservable(m -> m.flattenStreamAsObservable(Stream::of)); + } + + @Test + public void fusedEmpty() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of()) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + } + + @Test + public void fusedJust() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of(v)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void fusedMany() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1, 2, 3); + } + + @Test + public void fusedManyRejected() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1, 2, 3); + } + + @Test + public void fusedStreamAvailableLater() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + SingleSubject ss = SingleSubject.create(); + + ss + .flattenStreamAsObservable(v -> Stream.of(v, v + 1, v + 2)) + .subscribe(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertEmpty(); + + ss.onSuccess(1); + + to + .assertResult(1, 2, 3); + } + + @Test + public void fused() throws Throwable { + AtomicReference> qdr = new AtomicReference<>(); + + SingleSubject ss = SingleSubject.create(); + + ss + .flattenStreamAsObservable(Stream::of) + .subscribe(new Observer() { + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + @SuppressWarnings("unchecked") + public void onSubscribe(Disposable d) { + qdr.set((QueueDisposable)d); + } + }); + + QueueDisposable qd = qdr.get(); + + assertEquals(QueueFuseable.ASYNC, qd.requestFusion(QueueFuseable.ASYNC)); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + ss.onSuccess(1); + + assertFalse(qd.isEmpty()); + assertEquals(1, qd.poll().intValue()); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + qd.dispose(); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + } + + @Test + public void fused2() throws Throwable { + AtomicReference> qdr = new AtomicReference<>(); + + SingleSubject ss = SingleSubject.create(); + + ss + .flattenStreamAsObservable(v -> Stream.of(v, v + 1)) + .subscribe(new Observer() { + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + @SuppressWarnings("unchecked") + public void onSubscribe(Disposable d) { + qdr.set((QueueDisposable)d); + } + }); + + QueueDisposable qd = qdr.get(); + + assertEquals(QueueFuseable.ASYNC, qd.requestFusion(QueueFuseable.ASYNC)); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + ss.onSuccess(1); + + assertFalse(qd.isEmpty()); + assertEquals(1, qd.poll().intValue()); + + assertFalse(qd.isEmpty()); + assertEquals(2, qd.poll().intValue()); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + + qd.dispose(); + + assertTrue(qd.isEmpty()); + assertNull(qd.poll()); + } + + @Test + public void streamCloseCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.just(1) + .flattenStreamAsObservable(v -> Stream.of(v).onClose(() -> { throw new TestException(); })) + .test() + .assertResult(1); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void hasNextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + throw new TestException(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Single.just(1) + .flattenStreamAsObservable(v -> stream) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void nextThrowsInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + throw new TestException(); + } + }); + + Single.just(1) + .flattenStreamAsObservable(v -> stream) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelAfterHasNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestObserver to = new TestObserver<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + int count; + + @Override + public boolean hasNext() { + if (count++ > 0) { + to.dispose(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }); + + Single.just(1) + .flattenStreamAsObservable(v -> stream) + .subscribeWith(to) + .assertValuesOnly(1); + } + + @Test + public void cancelAfterNextInDrain() { + @SuppressWarnings("unchecked") + Stream stream = mock(Stream.class); + + TestObserver to = new TestObserver<>(); + + when(stream.iterator()).thenReturn(new Iterator() { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + to.dispose(); + return 1; + } + }); + + Single.just(1) + .flattenStreamAsObservable(v -> stream) + .subscribeWith(to) + .assertEmpty(); + } + + @Test + public void cancelSuccessRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + SingleSubject ss = SingleSubject.create(); + + TestObserver to = new TestObserver<>(); + + ss.flattenStreamAsObservable(Stream::of) + .subscribe(to); + + Runnable r1 = () -> ss.onSuccess(1); + Runnable r2 = () -> to.dispose(); + + TestHelper.race(r1, r2); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFromCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFromCompletionStageTest.java new file mode 100644 index 0000000000..c62996e586 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleFromCompletionStageTest.java @@ -0,0 +1,71 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleFromCompletionStageTest extends RxJavaTest { + + @Test + public void syncSuccess() { + Single.fromCompletionStage(CompletableFuture.completedFuture(1)) + .test() + .assertResult(1); + } + + @Test + public void syncFailure() { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new TestException()); + + Single.fromCompletionStage(cf) + .test() + .assertFailure(TestException.class); + } + + @Test + public void syncNull() { + Single.fromCompletionStage(CompletableFuture.completedFuture(null)) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void dispose() { + CompletableFuture cf = new CompletableFuture<>(); + + TestObserver to = Single.fromCompletionStage(cf) + .test(); + + to.assertEmpty(); + + to.dispose(); + + cf.complete(1); + + to.assertEmpty(); + } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Single.fromCompletionStage(new CompletableFuture<>())); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleMapOptionalTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleMapOptionalTest.java new file mode 100644 index 0000000000..56bc5dbae5 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleMapOptionalTest.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleMapOptionalTest extends RxJavaTest { + + @Test + public void successSuccess() { + Single.just(1) + .mapOptional(Optional::of) + .test() + .assertResult(1); + } + + @Test + public void successEmpty() { + Single.just(1) + .mapOptional(v -> Optional.empty()) + .test() + .assertResult(); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Function> f = mock(Function.class); + + Single.error(new TestException()) + .mapOptional(f) + .test() + .assertFailure(TestException.class); + + verify(f, never()).apply(any()); + } + + @Test + public void mapperCrash() { + Single.just(1) + .mapOptional(v -> { throw new TestException(); }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.never().mapOptional(Optional::of)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToMaybe(m -> m.mapOptional(Optional::of)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleToCompletionStageTest.java b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleToCompletionStageTest.java new file mode 100644 index 0000000000..2709b6ee40 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/jdk8/SingleToCompletionStageTest.java @@ -0,0 +1,153 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.jdk8; + +import static org.junit.Assert.*; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleToCompletionStageTest extends RxJavaTest { + + @Test + public void just() throws Exception { + Integer v = Single.just(1) + .toCompletionStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + } + + @Test + public void completableFutureCancels() throws Exception { + SingleSubject source = SingleSubject.create(); + + CompletableFuture cf = source + .toCompletionStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.cancel(true); + + assertTrue(cf.isCancelled()); + + assertFalse(source.hasObservers()); + } + + @Test + public void completableManualCompleteCancels() throws Exception { + SingleSubject source = SingleSubject.create(); + + CompletableFuture cf = source + .toCompletionStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.complete(1); + + assertTrue(cf.isDone()); + assertFalse(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + assertEquals((Integer)1, cf.get()); + } + + @Test + public void completableManualCompleteExceptionallyCancels() throws Exception { + SingleSubject source = SingleSubject.create(); + + CompletableFuture cf = source + .toCompletionStage() + .toCompletableFuture(); + + assertTrue(source.hasObservers()); + + cf.completeExceptionally(new TestException()); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + assertFalse(source.hasObservers()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void error() throws Exception { + CompletableFuture cf = Single.error(new TestException()) + .toCompletionStage() + .toCompletableFuture(); + + assertTrue(cf.isDone()); + assertTrue(cf.isCompletedExceptionally()); + assertFalse(cf.isCancelled()); + + TestHelper.assertError(cf, TestException.class); + } + + @Test + public void sourceIgnoresCancel() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Single() { + @Override + protected void subscribeActual(SingleObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + observer.onError(new TestException()); + } + } + .toCompletionStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void doubleOnSubscribe() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Integer v = new Single() { + @Override + protected void subscribeActual(SingleObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + } + } + .toCompletionStage() + .toCompletableFuture() + .get(); + + assertEquals((Integer)1, v); + + TestHelper.assertError(errors, 0, ProtocolViolationException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserverTest.java index c62f59f831..f48d0a574e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/BasicFuseableObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,11 +13,11 @@ package io.reactivex.rxjava3.internal.observers; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.annotations.Nullable; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.testsupport.TestObserverEx; @@ -25,7 +25,7 @@ public class BasicFuseableObserverTest extends RxJavaTest { @Test(expected = UnsupportedOperationException.class) public void offer() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); BasicFuseableObserver o = new BasicFuseableObserver(to) { @Nullable @Override @@ -48,7 +48,7 @@ protected boolean beforeDownstream() { } }; - o.onSubscribe(Disposables.disposed()); + o.onSubscribe(Disposable.disposed()); to.assertNotSubscribed(); @@ -57,7 +57,7 @@ protected boolean beforeDownstream() { @Test(expected = UnsupportedOperationException.class) public void offer2() { - BasicFuseableObserver o = new BasicFuseableObserver(new TestObserver()) { + BasicFuseableObserver o = new BasicFuseableObserver(new TestObserver<>()) { @Nullable @Override public Integer poll() throws Exception { diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposableTest.java index e5ed0fb605..d9bf994746 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/BasicQueueDisposableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserverTest.java index e871ec9639..3a65016574 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingFirstObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,8 +25,8 @@ public class BlockingFirstObserverTest extends RxJavaTest { @Test public void firstValueOnly() { - BlockingFirstObserver bf = new BlockingFirstObserver(); - Disposable d = Disposables.empty(); + BlockingFirstObserver bf = new BlockingFirstObserver<>(); + Disposable d = Disposable.empty(); bf.onSubscribe(d); bf.onNext(1); diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserverTest.java index dddbb1e962..0ea5b468ac 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingMultiObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,17 +27,17 @@ public class BlockingMultiObserverTest extends RxJavaTest { @Test public void dispose() { - BlockingMultiObserver bmo = new BlockingMultiObserver(); + BlockingMultiObserver bmo = new BlockingMultiObserver<>(); bmo.dispose(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); bmo.onSubscribe(d); } @Test public void blockingGetDefault() { - final BlockingMultiObserver bmo = new BlockingMultiObserver(); + final BlockingMultiObserver bmo = new BlockingMultiObserver<>(); Schedulers.single().scheduleDirect(new Runnable() { @Override @@ -51,7 +51,7 @@ public void run() { @Test public void blockingAwait() { - final BlockingMultiObserver bmo = new BlockingMultiObserver(); + final BlockingMultiObserver bmo = new BlockingMultiObserver<>(); Schedulers.single().scheduleDirect(new Runnable() { @Override @@ -65,7 +65,7 @@ public void run() { @Test public void blockingGetDefaultInterrupt() { - final BlockingMultiObserver bmo = new BlockingMultiObserver(); + final BlockingMultiObserver bmo = new BlockingMultiObserver<>(); Thread.currentThread().interrupt(); try { diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingObserverTest.java index f5d8fb6117..cbf8b7ce7c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/BlockingObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,9 +25,9 @@ public class BlockingObserverTest extends RxJavaTest { @Test public void dispose() { - Queue q = new ArrayDeque(); + Queue q = new ArrayDeque<>(); - BlockingObserver bo = new BlockingObserver(q); + BlockingObserver bo = new BlockingObserver<>(q); bo.dispose(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserverTest.java index e52ab8c023..46c4cc4463 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/CallbackCompletableObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ public final class CallbackCompletableObserverTest extends RxJavaTest { @Test public void emptyActionShouldReportNoCustomOnError() { - CallbackCompletableObserver o = new CallbackCompletableObserver(Functions.EMPTY_ACTION); + CallbackCompletableObserver o = new CallbackCompletableObserver(Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); assertFalse(o.hasCustomOnError()); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java new file mode 100644 index 0000000000..eb17ead3db --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/CompletableConsumersTest.java @@ -0,0 +1,221 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.CompletableSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final CompletableSubject processor = CompletableSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + @Test + public void onErrorNormal() { + + processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onComplete(); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList("OnComplete"), events); + + } + + @Test + public void onErrorError() { + + Disposable d = processor.subscribe(this, this, composite); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onComplete(); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList("OnComplete"), events); + + } + + @Test + public void onCompleteError() { + + processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = processor.subscribe(this, this, composite); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasObservers()); + } + + @Test + public void onErrorCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + processor.subscribe(this, t -> { + throw new IOException(t); + }, composite); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + processor.subscribe(new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }, this, composite); + + processor.onComplete(); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + new Completable() { + @Override + protected void subscribeActual( + CompletableObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + observer.onError(new IOException()); + } + }.subscribe(this, this, composite); + + assertEquals(Arrays.asList("OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserverTest.java index aa2e6e22e3..f6ebdb498f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/ConsumerSingleObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ public final class ConsumerSingleObserverTest extends RxJavaTest { @Test public void onErrorMissingShouldReportNoCustomOnError() { - ConsumerSingleObserver o = new ConsumerSingleObserver(Functions.emptyConsumer(), + ConsumerSingleObserver o = new ConsumerSingleObserver<>(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING); assertFalse(o.hasCustomOnError()); @@ -32,7 +32,7 @@ public void onErrorMissingShouldReportNoCustomOnError() { @Test public void customOnErrorShouldReportCustomOnError() { - ConsumerSingleObserver o = new ConsumerSingleObserver(Functions.emptyConsumer(), + ConsumerSingleObserver o = new ConsumerSingleObserver<>(Functions.emptyConsumer(), Functions.emptyConsumer()); assertTrue(o.hasCustomOnError()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java new file mode 100644 index 0000000000..19c5b7db27 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarDisposableTest.java @@ -0,0 +1,35 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.observers.TestObserver; + +public class DeferredScalarDisposableTest extends RxJavaTest { + + @Test + public void tryDispose() { + TestObserver to = new TestObserver<>(); + + DeferredScalarDisposable d = new DeferredScalarDisposable<>(to); + to.onSubscribe(d); + + assertTrue(d.tryDispose()); + assertFalse(d.tryDispose()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserverTest.java index 139d6fa0fc..165de9b467 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/DeferredScalarObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,8 +22,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.testsupport.*; @@ -50,13 +51,13 @@ public void onNext(Integer value) { public void normal() { List errors = TestHelper.trackPluginErrors(); try { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); TakeFirst source = new TakeFirst(to); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); assertTrue(d.isDisposed()); @@ -73,11 +74,11 @@ public void normal() { @Test public void error() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); TakeFirst source = new TakeFirst(to); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); source.onError(new TestException()); to.assertFailure(TestException.class); @@ -85,11 +86,11 @@ public void error() { @Test public void complete() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); TakeFirst source = new TakeFirst(to); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); source.onComplete(); to.assertResult(); @@ -97,11 +98,11 @@ public void complete() { @Test public void dispose() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); TakeFirst source = new TakeFirst(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -118,11 +119,11 @@ public void dispose() { public void fused() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); TakeFirst source = new TakeFirst(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -148,11 +149,11 @@ public void fused() { public void fusedReject() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); TakeFirst source = new TakeFirst(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -193,11 +194,11 @@ public void onNext(Integer value) { public void nonfusedTerminateMore() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(QueueFuseable.NONE); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.NONE); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -218,11 +219,11 @@ public void nonfusedTerminateMore() { public void nonfusedError() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(QueueFuseable.NONE); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.NONE); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -243,11 +244,11 @@ public void nonfusedError() { public void fusedTerminateMore() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -268,11 +269,11 @@ public void fusedTerminateMore() { public void fusedError() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -291,11 +292,11 @@ public void fusedError() { @Test public void disposed() { - TestObserverEx to = new TestObserverEx(QueueFuseable.NONE); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.NONE); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -309,7 +310,7 @@ public void disposed() { @Test public void disposedAfterOnNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); TakeLast source = new TakeLast(new Observer() { Disposable upstream; @@ -337,7 +338,7 @@ public void onComplete() { } }); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); source.onNext(1); source.onComplete(); @@ -346,11 +347,11 @@ public void onComplete() { @Test public void fusedEmpty() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -361,11 +362,11 @@ public void fusedEmpty() { @Test public void nonfusedEmpty() { - TestObserverEx to = new TestObserverEx(QueueFuseable.NONE); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.NONE); TakeLast source = new TakeLast(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); source.onSubscribe(d); @@ -376,7 +377,7 @@ public void nonfusedEmpty() { @Test public void customFusion() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); TakeLast source = new TakeLast(new Observer() { QueueDisposable d; @@ -417,7 +418,7 @@ public void onComplete() { } }); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); source.onNext(1); source.onComplete(); @@ -426,7 +427,7 @@ public void onComplete() { @Test public void customFusionClear() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); TakeLast source = new TakeLast(new Observer() { QueueDisposable d; @@ -456,7 +457,7 @@ public void onComplete() { } }); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); source.onNext(1); source.onComplete(); @@ -465,7 +466,7 @@ public void onComplete() { @Test public void offerThrow() { - TestObserverEx to = new TestObserverEx(QueueFuseable.NONE); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.NONE); TakeLast source = new TakeLast(to); @@ -474,7 +475,7 @@ public void offerThrow() { @Test public void customFusionDontConsume() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); TakeFirst source = new TakeFirst(new Observer() { QueueDisposable d; @@ -503,7 +504,7 @@ public void onComplete() { } }); - source.onSubscribe(Disposables.empty()); + source.onSubscribe(Disposable.empty()); source.onNext(1); to.assertNoValues().assertNoErrors().assertComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserverTest.java index 0de5f0dcaa..294892507b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/DisposableLambdaObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.internal.functions.Functions; @@ -32,8 +32,8 @@ public class DisposableLambdaObserverTest extends RxJavaTest { @Test public void doubleOnSubscribe() { - TestHelper.doubleOnSubscribe(new DisposableLambdaObserver( - new TestObserver(), Functions.emptyConsumer(), Functions.EMPTY_ACTION + TestHelper.doubleOnSubscribe(new DisposableLambdaObserver<>( + new TestObserver<>(), Functions.emptyConsumer(), Functions.EMPTY_ACTION )); } @@ -41,8 +41,8 @@ public void doubleOnSubscribe() { public void disposeCrash() { List errors = TestHelper.trackPluginErrors(); try { - DisposableLambdaObserver o = new DisposableLambdaObserver( - new TestObserver(), Functions.emptyConsumer(), + DisposableLambdaObserver o = new DisposableLambdaObserver<>( + new TestObserver<>(), Functions.emptyConsumer(), new Action() { @Override public void run() throws Exception { @@ -51,7 +51,7 @@ public void run() throws Exception { } ); - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); assertFalse(o.isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserverTest.java index e873326375..c58de8f0f9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/EmptyCompletableObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java new file mode 100644 index 0000000000..7499b99ca1 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserverTest.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.disposables.Disposable; + +public class FutureMultiObserverTest extends RxJavaTest { + + @Test + public void cancelBeforeOnSubscribe() { + FutureMultiObserver f = new FutureMultiObserver<>(); + + assertTrue(f.cancel(true)); + + Disposable d = Disposable.empty(); + + f.onSubscribe(d); + + assertTrue(d.isDisposed()); + } + + @Test + public void onCompleteJustAfterDispose() { + FutureMultiObserver f = new FutureMultiObserver<>(); + Disposable d = Disposable.empty(); + f.onSubscribe(d); + assertTrue(f.cancel(true)); + + f.onComplete(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java index 1edecae92c..8c475eb038 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,11 +22,9 @@ import org.junit.*; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.subscribers.FutureSubscriber; -import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -36,7 +34,7 @@ public class FutureObserverTest extends RxJavaTest { @Before public void before() { - fo = new FutureObserver(); + fo = new FutureObserver<>(); } @Test @@ -137,11 +135,11 @@ public void onSubscribe() throws Exception { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); fo.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); fo.onSubscribe(d2); @@ -157,7 +155,7 @@ public void onSubscribe() throws Exception { @Test public void cancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fo = new FutureSubscriber(); + final FutureObserver fo = new FutureObserver<>(); Runnable r = new Runnable() { @Override @@ -188,7 +186,7 @@ public void onErrorCancelRace() { RxJavaPlugins.setErrorHandler(Functions.emptyConsumer()); try { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fo = new FutureSubscriber(); + final FutureObserver fo = new FutureObserver<>(); final TestException ex = new TestException(); @@ -218,10 +216,10 @@ public void onCompleteCancelRace() { RxJavaPlugins.setErrorHandler(Functions.emptyConsumer()); try { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fo = new FutureSubscriber(); + final FutureObserver fo = new FutureObserver<>(); if (i % 3 == 0) { - fo.onSubscribe(new BooleanSubscription()); + fo.onSubscribe(Disposable.empty()); } if (i % 2 == 0) { @@ -288,6 +286,22 @@ public void onCompleteOnError() throws Exception { } } + @Test + public void onNextCompleteOnError() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + fo.onNext(1); + fo.onComplete(); + fo.onError(new TestException("One")); + + assertEquals((Integer)1, fo.get(5, TimeUnit.MILLISECONDS)); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } + @Test public void cancelOnError() throws Exception { List errors = TestHelper.trackPluginErrors(); @@ -364,4 +378,22 @@ public void getTimedOut() throws Exception { assertEquals(timeoutMessage(1, TimeUnit.NANOSECONDS), expected.getMessage()); } } + + @Test + public void cancelOnSubscribeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final FutureObserver fo = new FutureObserver<>(); + + Runnable r = new Runnable() { + @Override + public void run() { + fo.cancel(false); + } + }; + + Disposable d = Disposable.empty(); + + TestHelper.race(r, () -> fo.onSubscribe(d)); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserverTest.java index eb11be24c3..db8b414347 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java new file mode 100644 index 0000000000..549c72534a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/InnerQueuedObserverTest.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.observers; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class InnerQueuedObserverTest extends RxJavaTest { + + @Test + public void dispose() { + TestHelper.checkDisposed(new InnerQueuedObserver<>(null, 1)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/LambdaObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/LambdaObserverTest.java index 7cfd46352a..7ce4307419 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/LambdaObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/LambdaObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,20 +35,20 @@ public class LambdaObserverTest extends RxJavaTest { @Test public void onSubscribeThrows() { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaObserver o = new LambdaObserver(new Consumer() { + LambdaObserver o = new LambdaObserver<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -72,20 +72,20 @@ public void accept(Disposable d) throws Exception { @Test public void onNextThrows() { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaObserver o = new LambdaObserver(new Consumer() { + LambdaObserver o = new LambdaObserver<>(new Consumer() { @Override public void accept(Object v) throws Exception { throw new TestException(); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -111,20 +111,20 @@ public void onErrorThrows() { List errors = TestHelper.trackPluginErrors(); try { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaObserver o = new LambdaObserver(new Consumer() { + LambdaObserver o = new LambdaObserver<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - throw new TestException("Inner"); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + throw new TestException("Inner"); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -157,20 +157,20 @@ public void onCompleteThrows() { List errors = TestHelper.trackPluginErrors(); try { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaObserver o = new LambdaObserver(new Consumer() { + LambdaObserver o = new LambdaObserver<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { throw new TestException(); @@ -202,9 +202,9 @@ public void badSourceOnSubscribe() { Observable source = new Observable() { @Override public void subscribeActual(Observer observer) { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); assertFalse(d1.isDisposed()); @@ -215,20 +215,20 @@ public void subscribeActual(Observer observer) { } }; - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaObserver o = new LambdaObserver(new Consumer() { + LambdaObserver o = new LambdaObserver<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -256,7 +256,7 @@ public void badSourceEmitAfterDone() { Observable source = new Observable() { @Override public void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); @@ -266,20 +266,20 @@ public void subscribeActual(Observer observer) { } }; - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaObserver o = new LambdaObserver(new Consumer() { + LambdaObserver o = new LambdaObserver<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -304,7 +304,7 @@ public void accept(Disposable d) throws Exception { public void onNextThrowsCancelsUpstream() { PublishSubject ps = PublishSubject.create(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); ps.subscribe(new Consumer() { @Override @@ -333,9 +333,9 @@ public void accept(Throwable e) throws Exception { public void onSubscribeThrowsCancelsUpstream() { PublishSubject ps = PublishSubject.create(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); - ps.subscribe(new LambdaObserver(new Consumer() { + ps.subscribe(new LambdaObserver<>(new Consumer() { @Override public void accept(Integer v) throws Exception { } @@ -363,7 +363,7 @@ public void accept(Disposable d) throws Exception { @Test public void onErrorMissingShouldReportNoCustomOnError() { - LambdaObserver o = new LambdaObserver(Functions.emptyConsumer(), + LambdaObserver o = new LambdaObserver<>(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION, Functions.emptyConsumer()); @@ -373,7 +373,7 @@ public void onErrorMissingShouldReportNoCustomOnError() { @Test public void customOnErrorShouldReportCustomOnError() { - LambdaObserver o = new LambdaObserver(Functions.emptyConsumer(), + LambdaObserver o = new LambdaObserver<>(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.emptyConsumer()); @@ -385,9 +385,9 @@ public void customOnErrorShouldReportCustomOnError() { public void disposedObserverShouldReportErrorOnGlobalErrorHandler() { List errors = TestHelper.trackPluginErrors(); try { - final List observerErrors = Collections.synchronizedList(new ArrayList()); + final List observerErrors = Collections.synchronizedList(new ArrayList<>()); - LambdaObserver o = new LambdaObserver(Functions.emptyConsumer(), + LambdaObserver o = new LambdaObserver<>(Functions.emptyConsumer(), new Consumer() { @Override public void accept(Throwable t) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java new file mode 100644 index 0000000000..92b32c000e --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/MaybeConsumersTest.java @@ -0,0 +1,273 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final MaybeSubject processor = MaybeSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Maybe source, CompositeDisposable composite, + Consumer onSuccess, Consumer onError, Action onComplete) { + return source.subscribe(onSuccess, onError, onComplete, composite); + } + + @Test + public void onSuccessNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onComplete(); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList("OnComplete"), events); + + } + + @Test + public void onCompleteError() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasObservers()); + } + + @Test + public void onSuccessCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this, this); + + processor.onSuccess(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onErrorCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }, this); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, this, new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }); + + processor.onComplete(); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Maybe() { + @Override + protected void subscribeActual( + MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onComplete(); + + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(2); + observer.onComplete(); + observer.onError(new IOException()); + } + }, composite, this, this, this + ); + + assertEquals(Arrays.asList("OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java new file mode 100644 index 0000000000..b5d26340e7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/ObservableConsumersTest.java @@ -0,0 +1,324 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class ObservableConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final PublishSubject processor = PublishSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Observable source, CompositeDisposable composite, + Consumer onNext, Consumer onError, Action onComplete) { + return source.subscribe(onNext, onError, onComplete, composite); + } + + @Test + public void onNextNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteError() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasObservers()); + } + + @Test + public void onNextCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this, this); + + processor.onNext(1); + + assertTrue(errors.toString(), errors.isEmpty()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashOnError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }, this); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashNoError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, t -> { + throw new IOException(); + }, Functions.ON_ERROR_MISSING, () -> { }); + + processor.onNext(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, OnErrorNotImplementedException.class); + assertTrue(errors.get(0).getCause() instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, this, new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }); + + processor.onNext(1); + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Observable() { + @Override + protected void subscribeActual( + Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onComplete(); + + observer.onSubscribe(Disposable.empty()); + observer.onNext(2); + observer.onComplete(); + observer.onError(new IOException()); + } + }, composite, this, this, this + ); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserverTest.java index b8d82a3ed3..eb2523a466 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/QueueDrainObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,14 +17,14 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.*; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.testsupport.TestHelper; public class QueueDrainObserverTest extends RxJavaTest { static final QueueDrainObserver createUnordered(TestObserver to, final Disposable d) { - return new QueueDrainObserver(to, new SpscArrayQueue(4)) { + return new QueueDrainObserver(to, new SpscArrayQueue<>(4)) { @Override public void onNext(Integer t) { fastPathEmit(t, false, d); @@ -51,7 +51,7 @@ public void accept(Observer a, Integer v) { } static final QueueDrainObserver createOrdered(TestObserver to, final Disposable d) { - return new QueueDrainObserver(to, new SpscArrayQueue(4)) { + return new QueueDrainObserver(to, new SpscArrayQueue<>(4)) { @Override public void onNext(Integer t) { fastPathOrderedEmit(t, false, d); @@ -79,10 +79,10 @@ public void accept(Observer a, Integer v) { @Test public void unorderedSlowPath() { - TestObserver to = new TestObserver(); - Disposable d = Disposables.empty(); + TestObserver to = new TestObserver<>(); + Disposable d = Disposable.empty(); QueueDrainObserver qd = createUnordered(to, d); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); qd.enter(); qd.onNext(1); @@ -92,10 +92,10 @@ public void unorderedSlowPath() { @Test public void orderedSlowPath() { - TestObserver to = new TestObserver(); - Disposable d = Disposables.empty(); + TestObserver to = new TestObserver<>(); + Disposable d = Disposable.empty(); QueueDrainObserver qd = createOrdered(to, d); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); qd.enter(); qd.onNext(1); @@ -105,10 +105,10 @@ public void orderedSlowPath() { @Test public void orderedSlowPathNonEmptyQueue() { - TestObserver to = new TestObserver(); - Disposable d = Disposables.empty(); + TestObserver to = new TestObserver<>(); + Disposable d = Disposable.empty(); QueueDrainObserver qd = createOrdered(to, d); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); qd.queue.offer(0); qd.onNext(1); @@ -120,10 +120,10 @@ public void orderedSlowPathNonEmptyQueue() { public void unorderedOnNextRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - TestObserver to = new TestObserver(); - Disposable d = Disposables.empty(); + TestObserver to = new TestObserver<>(); + Disposable d = Disposable.empty(); final QueueDrainObserver qd = createUnordered(to, d); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); Runnable r1 = new Runnable() { @Override @@ -142,10 +142,10 @@ public void run() { public void orderedOnNextRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - TestObserver to = new TestObserver(); - Disposable d = Disposables.empty(); + TestObserver to = new TestObserver<>(); + Disposable d = Disposable.empty(); final QueueDrainObserver qd = createOrdered(to, d); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); Runnable r1 = new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java new file mode 100644 index 0000000000..a840d8f71a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/observers/SingleConsumersTest.java @@ -0,0 +1,192 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.observers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.functions.Consumer; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleConsumersTest implements Consumer { + + final CompositeDisposable composite = new CompositeDisposable(); + + final SingleSubject processor = SingleSubject.create(); + + final List events = new ArrayList<>(); + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Single source, CompositeDisposable composite, + Consumer onSuccess, Consumer onError) { + return source.subscribe(onSuccess, onError, composite); + } + + @Test + public void onSuccessNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onSuccess(1); + + assertEquals(0, composite.size()); + + assertEquals(Arrays.asList(1), events); + + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onError(new IOException()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onSuccessCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this); + + processor.onSuccess(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onErrorCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Single() { + @Override + protected void subscribeActual( + SingleObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(2); + observer.onError(new IOException()); + } + }, composite, this, this + ); + + assertEquals(Arrays.asList(1), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmbTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmbTest.java index 643ae8a880..0c9971017b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmbTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAmbTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public class CompletableAmbTest extends RxJavaTest { @Test public void ambLots() { - List ms = new ArrayList(); + List ms = new ArrayList<>(); for (int i = 0; i < 32; i++) { ms.add(Completable.never()); @@ -172,13 +172,13 @@ public void ambArrayOrder() { @Test public void ambRace() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); CompositeDisposable cd = new CompositeDisposable(); AtomicBoolean once = new AtomicBoolean(); Amb a = new Amb(once, cd, to); - a.onSubscribe(Disposables.empty()); + a.onSubscribe(Disposable.empty()); a.onComplete(); a.onComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableabTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableTest.java similarity index 93% rename from src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableabTest.java rename to src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableTest.java index 1137804fe2..070fa8cb4d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableabTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,13 +27,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; -public class CompletableAndThenCompletableabTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void andThenCompletableCompleteNull() { - Completable.complete() - .andThen((Completable) null); - } - +public class CompletableAndThenCompletableTest extends RxJavaTest { @Test public void andThenCompletableCompleteComplete() { Completable.complete() @@ -108,12 +102,12 @@ public void run() { .andThen(Completable.complete()) .test(true) .assertEmpty(); - assertEquals(1, completableRunCount.get()); + assertEquals(0, completableRunCount.get()); } @Test public void andThenFirstCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.fromRunnable(new Runnable() { @Override public void run() { @@ -129,7 +123,7 @@ public void run() { @Test public void andThenSecondCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.complete() .andThen(Completable.fromRunnable(new Runnable() { @Override @@ -183,4 +177,9 @@ public void run() throws Exception { assertFalse("The second Completable was interrupted!", interrupted[0]); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeCompletable(c -> c.andThen(c)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenTest.java index 41656dbf33..943d44b9f2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAndThenTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,12 +19,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableAndThenTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void andThenMaybeNull() { - Completable.complete() - .andThen((Maybe) null); - } - @Test public void andThenMaybeCompleteValue() { Completable.complete() diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAwaitTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAwaitTest.java index b7d6d61ea3..b614e2d79e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAwaitTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableAwaitTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableBlockingSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableBlockingSubscribeTest.java new file mode 100644 index 0000000000..a84f78ad73 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableBlockingSubscribeTest.java @@ -0,0 +1,331 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableBlockingSubscribeTest { + + @Test + public void noArgComplete() { + Completable.complete() + .blockingSubscribe(); + } + + @Test + public void noArgCompleteAsync() { + Completable.complete() + .delay(100, TimeUnit.MILLISECONDS) + .blockingSubscribe(); + } + + @Test + public void noArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Completable.error(new TestException()) + .blockingSubscribe(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void noArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Completable.error(new TestException()) + .delay(100, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void oneArgComplete() throws Throwable { + Action action = mock(Action.class); + + Completable.complete() + .blockingSubscribe(action); + + verify(action).run(); + } + + @Test + public void oneArgCompleteAsync() throws Throwable { + Action action = mock(Action.class); + + Completable.complete() + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(action); + + verify(action).run(); + } + + @Test + public void oneArgCompleteFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + doThrow(new TestException()).when(action).run(); + + Completable.complete() + .blockingSubscribe(action); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(action).run(); + }); + } + + @Test + public void oneArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + + Completable.error(new TestException()) + .blockingSubscribe(action); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(action, never()).run(); + }); + } + + @Test + public void oneArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + + Completable.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(action); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(action, never()).run(); + }); + } + + @Test + public void twoArgComplete() throws Throwable { + Action action = mock(Action.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Completable.complete() + .blockingSubscribe(action, consumer); + + verify(action).run(); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgCompleteAsync() throws Throwable { + Action action = mock(Action.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Completable.complete() + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(action, consumer); + + verify(action).run(); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgCompleteFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + doThrow(new TestException()).when(action).run(); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Completable.complete() + .blockingSubscribe(action, consumer); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(action).run(); + verify(consumer, never()).accept(any()); + }); + } + + @Test + public void twoArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Completable.error(new TestException()) + .blockingSubscribe(action, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(action, never()).run(); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Completable.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(action, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(action, never()).run(); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgErrorFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action action = mock(Action.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + doThrow(new TestException()).when(consumer).accept(any()); + + Completable.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(action, consumer); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(action, never()).run(); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgInterrupted() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action onDispose = mock(Action.class); + + Action action = mock(Action.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Thread.currentThread().interrupt(); + + Completable.never() + .doOnDispose(onDispose) + .blockingSubscribe(action, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(onDispose).run(); + verify(action, never()).run(); + verify(consumer).accept(any(InterruptedException.class)); + }); + } + + @Test + public void observerComplete() { + TestObserver to = new TestObserver<>(); + + Completable.complete() + .blockingSubscribe(to); + + to.assertResult(); + } + + @Test + public void observerCompleteAsync() { + TestObserver to = new TestObserver<>(); + + Completable.complete() + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(to); + + to.assertResult(); + } + + @Test + public void observerError() { + TestObserver to = new TestObserver<>(); + + Completable.error(new TestException()) + .blockingSubscribe(to); + + to.assertFailure(TestException.class); + } + + @Test + public void observerErrorAsync() { + TestObserver to = new TestObserver<>(); + + Completable.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(to); + + to.assertFailure(TestException.class); + } + + @Test + public void observerDispose() throws Throwable { + Action onDispose = mock(Action.class); + + TestObserver to = new TestObserver<>(); + to.dispose(); + + Completable.never() + .doOnDispose(onDispose) + .blockingSubscribe(to); + + to.assertEmpty(); + + verify(onDispose).run(); + } + + @Test + public void ovserverInterrupted() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action onDispose = mock(Action.class); + + TestObserver to = new TestObserver<>(); + + Thread.currentThread().interrupt(); + + Completable.never() + .doOnDispose(onDispose) + .blockingSubscribe(to); + + assertTrue("" + errors, errors.isEmpty()); + + verify(onDispose).run(); + to.assertFailure(InterruptedException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCacheTest.java index 08c93ae7d6..3e6193a2e9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCacheTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -86,7 +86,7 @@ public void error() { public void crossDispose() { PublishSubject ps = PublishSubject.create(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); final TestObserver to2 = new TestObserver() { @Override @@ -111,7 +111,7 @@ public void onComplete() { public void crossDisposeOnError() { PublishSubject ps = PublishSubject.create(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); final TestObserver to2 = new TestObserver() { @Override @@ -174,9 +174,9 @@ public void subscribeRace() { final Completable c = ps.ignoreElements().cache(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -209,7 +209,7 @@ public void subscribeDisposeRace() { final TestObserver to1 = c.test(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -237,7 +237,7 @@ public void run() { public void doubleDispose() { PublishSubject ps = PublishSubject.create(); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); ps.ignoreElements().cache() .subscribe(new CompletableObserver() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArrayDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArrayDelayErrorTest.java new file mode 100644 index 0000000000..418afc70cc --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatArrayDelayErrorTest.java @@ -0,0 +1,43 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.mockito.Mockito.*; +import org.junit.Test; + +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Action; + +public class CompletableConcatArrayDelayErrorTest { + + @Test + public void normal() throws Throwable { + Action action1 = mock(Action.class); + Action action2 = mock(Action.class); + + Completable.concatArrayDelayError( + Completable.fromAction(action1), + Completable.error(new TestException()), + Completable.fromAction(action2) + ) + .test() + .assertFailure(TestException.class); + + verify(action1).run(); + + verify(action2).run(); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatDelayErrorTest.java new file mode 100644 index 0000000000..8a9f9477d8 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatDelayErrorTest.java @@ -0,0 +1,82 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.mockito.Mockito.*; + +import java.util.Arrays; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Action; + +public class CompletableConcatDelayErrorTest { + + @Test + public void normalIterable() throws Throwable { + Action action1 = mock(Action.class); + Action action2 = mock(Action.class); + + Completable.concatDelayError(Arrays.asList( + Completable.fromAction(action1), + Completable.error(new TestException()), + Completable.fromAction(action2) + )) + .test() + .assertFailure(TestException.class); + + verify(action1).run(); + + verify(action2).run(); + } + + @Test + public void normalPublisher() throws Throwable { + Action action1 = mock(Action.class); + Action action2 = mock(Action.class); + + Completable.concatDelayError(Flowable.fromArray( + Completable.fromAction(action1), + Completable.error(new TestException()), + Completable.fromAction(action2) + )) + .test() + .assertFailure(TestException.class); + + verify(action1).run(); + + verify(action2).run(); + } + + @Test + public void normalPublisherPrefetch() throws Throwable { + Action action1 = mock(Action.class); + Action action2 = mock(Action.class); + + Completable.concatDelayError(Flowable.fromArray( + Completable.fromAction(action1), + Completable.error(new TestException()), + Completable.fromAction(action2) + ), 1) + .test() + .assertFailure(TestException.class); + + verify(action1).run(); + + verify(action2).run(); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java index 19cff53d90..11bc1a9b06 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableConcatTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,11 @@ import java.util.*; import java.util.concurrent.CountDownLatch; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; @@ -52,9 +52,9 @@ public void subscribe(Subscriber s) { }), 1 ) .test() - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); - TestHelper.assertError(errors, 0, MissingBackpressureException.class); + TestHelper.assertError(errors, 0, QueueOverflowException.class); } finally { RxJavaPlugins.reset(); } @@ -167,12 +167,12 @@ public void arrayCancelled() { @Test public void arrayFirstCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.concatArray(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); to.dispose(); observer.onComplete(); } @@ -191,12 +191,12 @@ public void iterableCancelled() { @Test public void iterableFirstCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.concat(Arrays.asList(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); to.dispose(); observer.onComplete(); } @@ -215,7 +215,7 @@ public void arrayCancelRace() { final Completable c = Completable.concatArray(a); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -244,7 +244,7 @@ public void iterableCancelRace() { final Completable c = Completable.concat(Arrays.asList(a)); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -300,4 +300,9 @@ public void run() throws Exception { assertFalse("The second Completable was interrupted!", interrupted[0]); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToCompletable(f -> Completable.concat(f)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreateTest.java index 8b7993609f..bda1166ef9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableCreateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,16 +29,11 @@ public class CompletableCreateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void nullArgument() { - Completable.create(null); - } - @Test public void basic() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Completable.create(new CompletableOnSubscribe() { @Override @@ -65,8 +60,8 @@ public void subscribe(CompletableEmitter e) throws Exception { public void basicWithCancellable() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d1 = Disposables.empty(); - final Disposable d2 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); + final Disposable d2 = Disposable.empty(); Completable.create(new CompletableOnSubscribe() { @Override @@ -100,7 +95,7 @@ public void cancel() throws Exception { public void basicWithError() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Completable.create(new CompletableOnSubscribe() { @Override @@ -162,7 +157,7 @@ public void onErrorThrows() { Completable.create(new CompletableOnSubscribe() { @Override public void subscribe(CompletableEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { @@ -199,7 +194,7 @@ public void onCompleteThrows() { Completable.create(new CompletableOnSubscribe() { @Override public void subscribe(CompletableEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelaySubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelaySubscriptionTest.java index d467d8e2cf..6e524eff27 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelaySubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelaySubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelayTest.java index 117bdb0bcf..c84972863d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDelayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public void delayCustomScheduler() { @Test public void onErrorCalledOnScheduler() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference thread = new AtomicReference(); + final AtomicReference thread = new AtomicReference<>(); Completable.error(new Exception()) .delay(0, TimeUnit.MILLISECONDS, Schedulers.newThread()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetachTest.java index 5225359554..5373499d30 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDetachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -64,8 +64,8 @@ public void onComplete() { @Test public void cancelDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Completable() { @Override @@ -90,8 +90,8 @@ protected void subscribeActual(CompletableObserver observer) { @Test public void completeDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Completable() { @Override @@ -116,8 +116,8 @@ protected void subscribeActual(CompletableObserver observer) { @Test public void errorDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Completable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java index 32b5a85aa9..73a4e58c77 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDisposeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.TestScheduler; +import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -138,4 +138,9 @@ public void run() throws Exception { assertEquals(0, call[0]); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeCompletable(c -> c.unsubscribeOn(Schedulers.computation())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinallyTest.java index cd0df19aaf..14bd307b37 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoFinallyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -65,11 +65,6 @@ public Completable apply(Completable f) throws Exception { }); } - @Test(expected = NullPointerException.class) - public void nullAction() { - Completable.complete().doFinally(null); - } - @Test public void actionThrows() { List errors = TestHelper.trackPluginErrors(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnLifecycleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnLifecycleTest.java new file mode 100644 index 0000000000..1f66038f19 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnLifecycleTest.java @@ -0,0 +1,153 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.CompletableSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableDoOnLifecycleTest extends RxJavaTest { + + @Test + public void empty() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Completable.complete() + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertResult(); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Completable.error(new TestException()) + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertFailure(TestException.class); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void onSubscribeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + doThrow(new TestException("First")).when(onSubscribe).accept(any()); + + Disposable bs = Disposable.empty(); + + new Completable() { + @Override + protected void subscribeActual(CompletableObserver observer) { + observer.onSubscribe(bs); + observer.onError(new TestException("Second")); + observer.onComplete(); + } + } + .doOnLifecycle(onSubscribe, onDispose) + .to(TestHelper.testConsumer()) + .assertFailureAndMessage(TestException.class, "First"); + + assertTrue(bs.isDisposed()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "Second"); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + }); + } + + @Test + public void onDisposeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + doThrow(new TestException("First")).when(onDispose).run(); + + CompletableSubject cs = CompletableSubject.create(); + + TestObserver to = cs + .doOnLifecycle(onSubscribe, onDispose) + .test(); + + assertTrue(cs.hasObservers()); + + to.dispose(); + + assertFalse(cs.hasObservers()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "First"); + + verify(onSubscribe).accept(any()); + verify(onDispose).run(); + }); + } + + @Test + public void dispose() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + CompletableSubject cs = CompletableSubject.create(); + + TestObserver to = cs + .doOnLifecycle(onSubscribe, onDispose) + .test(); + + assertTrue(cs.hasObservers()); + + to.dispose(); + + assertFalse(cs.hasObservers()); + + verify(onSubscribe).accept(any()); + verify(onDispose).run(); + } + + @Test + public void isDisposed() { + TestHelper.checkDisposed(CompletableSubject.create().doOnLifecycle(d -> { }, () -> { })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeCompletable(m -> m.doOnLifecycle(d -> { }, () -> { })); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnTest.java index fea4bcb84e..6f9698afbf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableDoOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -82,7 +82,7 @@ public void run() throws Exception { public void onSubscribeCrash() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable bs = Disposables.empty(); + final Disposable bs = Disposable.empty(); new Completable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java index fb45fc3bad..e697a91d34 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromActionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.completable; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; import java.util.concurrent.atomic.AtomicInteger; @@ -22,13 +23,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableFromActionTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromActionNull() { - Completable.fromAction(null); - } - @Test public void fromAction() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -113,7 +111,7 @@ public void run() throws Exception { .test(true) .assertEmpty(); - assertEquals(1, calls.get()); + assertEquals(0, calls.get()); } @Test @@ -129,6 +127,44 @@ public void run() throws Exception { .test(true) .assertEmpty(); - assertEquals(1, calls.get()); + assertEquals(0, calls.get()); + } + + @Test + public void disposedUpfront() throws Throwable { + Action run = mock(Action.class); + + Completable.fromAction(run) + .test(true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void disposeWhileRunningComplete() { + TestObserver to = new TestObserver<>(); + + Completable.fromAction(() -> { + to.dispose(); + }) + .subscribeWith(to) + .assertEmpty(); + } + + @Test + public void disposeWhileRunningError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestObserver to = new TestObserver<>(); + + Completable.fromAction(() -> { + to.dispose(); + throw new TestException(); + }) + .subscribeWith(to) + .assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java index da082c514d..940444f7a9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromCallableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,13 +29,9 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class CompletableFromCallableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromCallableNull() { - Completable.fromCallable(null); - } @Test public void fromCallable() { @@ -142,7 +138,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromCallableObservable .subscribeOn(Schedulers.computation()) @@ -166,6 +162,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { } @Test + @SuppressUndeliverable public void fromActionErrorsDisposed() { final AtomicInteger calls = new AtomicInteger(); Completable.fromCallable(new Callable() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromMaybeTest.java index 9158d449c3..db201e07a7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,6 @@ import io.reactivex.rxjava3.core.*; public class CompletableFromMaybeTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromMaybeNull() { - Completable.fromMaybe(null); - } - @Test public void fromMaybe() { Completable.fromMaybe(Maybe.just(1)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservableTest.java index 9869ed04db..5fc8ba08c6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,6 @@ import io.reactivex.rxjava3.core.*; public class CompletableFromObservableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromObservableNull() { - Completable.fromObservable(null); - } - @Test public void fromObservable() { Completable.fromObservable(Observable.just(1)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisherTest.java index 7cfd899262..177dc4e156 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,11 +20,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableFromPublisherTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromPublisherNull() { - Completable.fromPublisher(null); - } - @Test public void fromPublisher() { Completable.fromPublisher(Flowable.just(1)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java index 276ae320b6..2b3e9a6514 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromRunnableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,7 @@ package io.reactivex.rxjava3.internal.operators.completable; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; import java.util.concurrent.atomic.AtomicInteger; @@ -21,13 +22,10 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableFromRunnableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromRunnableNull() { - Completable.fromRunnable(null); - } - @Test public void fromRunnable() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -112,7 +110,7 @@ public void run() { .test(true) .assertEmpty(); - assertEquals(1, calls.get()); + assertEquals(0, calls.get()); } @Test @@ -128,6 +126,44 @@ public void run() { .test(true) .assertEmpty(); - assertEquals(1, calls.get()); + assertEquals(0, calls.get()); + } + + @Test + public void disposedUpfront() throws Throwable { + Runnable run = mock(Runnable.class); + + Completable.fromRunnable(run) + .test(true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void disposeWhileRunningComplete() { + TestObserver to = new TestObserver<>(); + + Completable.fromRunnable(() -> { + to.dispose(); + }) + .subscribeWith(to) + .assertEmpty(); + } + + @Test + public void disposeWhileRunningError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestObserver to = new TestObserver<>(); + + Completable.fromRunnable(() -> { + to.dispose(); + throw new TestException(); + }) + .subscribeWith(to) + .assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingleTest.java index f9c6e82676..14e5a29ac9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,6 @@ import io.reactivex.rxjava3.core.*; public class CompletableFromSingleTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromSingleNull() { - Completable.fromSingle(null); - } - @Test public void fromSingle() { Completable.fromSingle(Single.just(1)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java index c7943de244..453fc4c2c0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableFromSupplierTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,15 +30,10 @@ import io.reactivex.rxjava3.functions.Supplier; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class CompletableFromSupplierTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromSupplierNull() { - Completable.fromSupplier(null); - } - @Test public void fromSupplier() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -144,7 +139,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromSupplierObservable .subscribeOn(Schedulers.computation()) @@ -168,6 +163,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { } @Test + @SuppressUndeliverable public void fromActionErrorsDisposed() { final AtomicInteger calls = new AtomicInteger(); Completable.fromSupplier(new Supplier() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHideTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHideTest.java index 59352d76a5..16afcaa075 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHideTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableHideTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLiftTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLiftTest.java index 4f0ba23ce8..21cf3fb305 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLiftTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableLiftTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterializeTest.java index 23b4e25731..8c301f50fa 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMaterializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ public class CompletableMaterializeTest extends RxJavaTest { @Test - @SuppressWarnings("unchecked") public void error() { TestException ex = new TestException(); Completable.error(ex) @@ -34,7 +33,6 @@ public void error() { } @Test - @SuppressWarnings("unchecked") public void empty() { Completable.complete() .materialize() diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java index 67253bceb0..cf3dc03c69 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,11 +14,14 @@ package io.reactivex.rxjava3.internal.operators.completable; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.internal.operators.completable.CompletableMergeIterable.MergeCompletableObserver; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.PublishSubject; @@ -67,7 +70,7 @@ public void run() { @Test public void cancelAfterHasNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.merge(new Iterable() { @Override @@ -97,7 +100,7 @@ public void remove() { @Test public void cancelAfterNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.merge(new Iterable() { @Override @@ -124,4 +127,9 @@ public void remove() { to.assertEmpty(); } + + @Test + public void dispose() { + TestHelper.checkDisposed(new MergeCompletableObserver(new TestObserver(), new CompositeDisposable(), new AtomicInteger())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java index 6a7c9aa70d..38166ea659 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableMergeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,6 +16,7 @@ import static org.junit.Assert.*; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.reactivestreams.Subscriber; @@ -25,9 +26,11 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.util.AtomicThrowable; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.CompletableSubject; import io.reactivex.rxjava3.testsupport.*; public class CompletableMergeTest extends RxJavaTest { @@ -43,12 +46,12 @@ public void invalidPrefetch() { @Test public void cancelAfterFirst() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.mergeArray(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); to.dispose(); } @@ -60,12 +63,12 @@ protected void subscribeActual(CompletableObserver observer) { @Test public void cancelAfterFirstDelayError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.mergeArrayDelayError(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); to.dispose(); } @@ -84,7 +87,7 @@ public void onErrorAfterComplete() { Completable.mergeArrayDelayError(Completable.complete(), new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); co[0] = observer; } @@ -410,7 +413,7 @@ public void innerDoubleOnError() { Completable.mergeDelayError(Flowable.just(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); o[0] = observer; } @@ -428,12 +431,12 @@ protected void subscribeActual(CompletableObserver observer) { @Test public void innerIsDisposed() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.mergeDelayError(Flowable.just(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); to.dispose(); @@ -494,7 +497,7 @@ public void delayErrorIterableCancel() { @Test public void delayErrorIterableCancelAfterHasNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.mergeDelayError(new Iterable() { @Override @@ -525,7 +528,7 @@ public void remove() { @Test public void delayErrorIterableCancelAfterNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Completable.mergeDelayError(new Iterable() { @Override @@ -593,4 +596,33 @@ public Completable apply(Flowable upstream) { } }); } + + @Test + public void iterableCompleteLater() { + CompletableSubject cs = CompletableSubject.create(); + + TestObserver to = Completable.mergeDelayError(Arrays.asList(cs, cs, cs)) + .test(); + + to.assertEmpty(); + + cs.onComplete(); + + to.assertResult(); + } + + @Test + public void terminalDisposed() { + TestHelper.checkDisposed(new CompletableMergeArrayDelayError.TryTerminateAndReportDisposable(new AtomicThrowable())); + } + + @Test + public void innerDisposed() { + TestHelper.checkDisposed(new CompletableMergeArray.InnerCompletableObserver(new TestObserver(), new AtomicBoolean(), new CompositeDisposable(), 1)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToCompletable(f -> Completable.merge(f)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOnTest.java index 6d5cc21064..f4613f5c59 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableObserveOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorXTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorXTest.java index 4a1333b530..d7b1cae653 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorXTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableOnErrorXTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,10 +15,16 @@ import static org.junit.Assert.assertEquals; +import java.io.IOException; + import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.subjects.CompletableSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableOnErrorXTest extends RxJavaTest { @@ -46,4 +52,55 @@ public CompletableSource apply(Throwable e) throws Exception { assertEquals(0, call[0]); } + + @Test + public void onErrorReturnConst() { + Completable.error(new TestException()) + .onErrorReturnItem(1) + .test() + .assertResult(1); + } + + @Test + public void onErrorReturn() { + Completable.error(new TestException()) + .onErrorReturn(Functions.justFunction(1)) + .test() + .assertResult(1); + } + + @Test + public void onErrorReturnFunctionThrows() { + TestHelper.assertCompositeExceptions(Completable.error(new TestException()) + .onErrorReturn(new Function() { + @Override + public Object apply(Throwable v) throws Exception { + throw new IOException(); + } + }) + .to(TestHelper.testConsumer()), TestException.class, IOException.class); + } + + @Test + public void onErrorReturnEmpty() { + Completable.complete() + .onErrorReturnItem(2) + .test() + .assertResult(); + } + + @Test + public void onErrorReturnDispose() { + TestHelper.checkDisposed(CompletableSubject.create().onErrorReturnItem(1)); + } + + @Test + public void onErrorReturnDoubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeCompletableToMaybe(new Function>() { + @Override + public MaybeSource apply(Completable v) throws Exception { + return v.onErrorReturnItem(1); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeekTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeekTest.java index ddc5a16056..827599e353 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeekTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletablePeekTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableRepeatWhenTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableRepeatWhenTest.java index 6a80eda644..d9a262b317 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableRepeatWhenTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableRepeatWhenTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNextTest.java index 5670264378..a8f0390c63 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableResumeNextTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,18 +13,19 @@ package io.reactivex.rxjava3.internal.operators.completable; +import static org.mockito.Mockito.*; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; -import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableResumeNextTest extends RxJavaTest { @Test - public void resumeWithError() { + public void resumeNextError() { Completable.error(new TestException()) .onErrorResumeNext(Functions.justFunction(Completable.error(new TestException("second")))) .to(TestHelper.testConsumer()) @@ -58,4 +59,28 @@ public void disposed() { .onErrorResumeNext(Functions.justFunction(Completable.never())) ); } + + @Test + public void resumeWithNoError() throws Throwable { + Action action = mock(Action.class); + + Completable.complete() + .onErrorResumeWith(Completable.fromAction(action)) + .test() + .assertResult(); + + verify(action, never()).run(); + } + + @Test + public void resumeWithError() throws Throwable { + Action action = mock(Action.class); + + Completable.error(new TestException()) + .onErrorResumeWith(Completable.fromAction(action)) + .test() + .assertResult(); + + verify(action).run(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSafeSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSafeSubscribeTest.java new file mode 100644 index 0000000000..40c618ac0a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSafeSubscribeTest.java @@ -0,0 +1,148 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import org.junit.Test; +import org.mockito.InOrder; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class CompletableSafeSubscribeTest { + + @Test + public void normalError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + CompletableObserver consumer = mock(CompletableObserver.class); + + Completable.error(new TestException()) + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onError(any(TestException.class)); + order.verifyNoMoreInteractions(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void normalEmpty() throws Throwable { + TestHelper.withErrorTracking(errors -> { + CompletableObserver consumer = mock(CompletableObserver.class); + + Completable.complete() + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onComplete(); + order.verifyNoMoreInteractions(); + }); + } + + @Test + public void onSubscribeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + CompletableObserver consumer = mock(CompletableObserver.class); + doThrow(new TestException()).when(consumer).onSubscribe(any()); + + Disposable d = Disposable.empty(); + + new Completable() { + @Override + protected void subscribeActual(@NonNull CompletableObserver observer) { + observer.onSubscribe(d); + // none of the following should arrive at the consumer + observer.onError(new IOException()); + observer.onComplete(); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verifyNoMoreInteractions(); + + assertTrue(d.isDisposed()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + TestHelper.assertUndeliverable(errors, 1, IOException.class); + }); + } + + @Test + public void onErrorCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + CompletableObserver consumer = mock(CompletableObserver.class); + doThrow(new TestException()).when(consumer).onError(any()); + + new Completable() { + @Override + protected void subscribeActual(@NonNull CompletableObserver observer) { + observer.onSubscribe(Disposable.empty()); + // none of the following should arrive at the consumer + observer.onError(new IOException()); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onError(any(IOException.class)); + order.verifyNoMoreInteractions(); + + TestHelper.assertError(errors, 0, CompositeException.class); + + CompositeException compositeException = (CompositeException)errors.get(0); + TestHelper.assertError(compositeException.getExceptions(), 0, IOException.class); + TestHelper.assertError(compositeException.getExceptions(), 1, TestException.class); + }); + } + + @Test + public void onCompleteCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + CompletableObserver consumer = mock(CompletableObserver.class); + doThrow(new TestException()).when(consumer).onComplete(); + + new Completable() { + @Override + protected void subscribeActual(@NonNull CompletableObserver observer) { + observer.onSubscribe(Disposable.empty()); + // none of the following should arrive at the consumer + observer.onComplete(); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onComplete(); + order.verifyNoMoreInteractions(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSequenceEqualTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSequenceEqualTest.java new file mode 100644 index 0000000000..9e3fd56b99 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSequenceEqualTest.java @@ -0,0 +1,43 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.exceptions.TestException; + +public class CompletableSequenceEqualTest { + + @Test + public void bothComplete() { + Completable.sequenceEqual(Completable.complete(), Completable.complete()) + .test() + .assertResult(true); + } + + @Test + public void firstFails() { + Completable.sequenceEqual(Completable.error(new TestException()), Completable.complete()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void secondFails() { + Completable.sequenceEqual(Completable.complete(), Completable.error(new TestException())) + .test() + .assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableStartWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableStartWithTest.java new file mode 100644 index 0000000000..4b2ed6d588 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableStartWithTest.java @@ -0,0 +1,67 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class CompletableStartWithTest { + + @Test + public void singleNormal() { + Completable.complete().startWith(Single.just(1)) + .test() + .assertResult(1); + } + + @Test + public void singleError() { + Runnable run = mock(Runnable.class); + + Completable.fromRunnable(run).startWith(Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void maybeNormal() { + Completable.complete().startWith(Maybe.just(1)) + .test() + .assertResult(1); + } + + @Test + public void maybeEmptyNormal() { + Completable.complete().startWith(Maybe.empty()) + .test() + .assertResult(); + } + + @Test + public void maybeError() { + Runnable run = mock(Runnable.class); + + Completable.fromRunnable(run).startWith(Maybe.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOnTest.java index 22d826fc70..3c20a12588 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeTest.java index 7c140db31d..f0fded5996 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSubscribeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSwitchOnNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSwitchOnNextTest.java new file mode 100644 index 0000000000..312f9653ed --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableSwitchOnNextTest.java @@ -0,0 +1,132 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.CompletableSubject; + +public class CompletableSwitchOnNextTest extends RxJavaTest { + + @Test + public void normal() { + Runnable run = mock(Runnable.class); + + Completable.switchOnNext( + Flowable.range(1, 10) + .map(v -> { + if (v % 2 == 0) { + return Completable.fromRunnable(run); + } + return Completable.complete(); + }) + ) + .test() + .assertResult(); + + verify(run, times(5)).run(); + } + + @Test + public void normalDelayError() { + Runnable run = mock(Runnable.class); + + Completable.switchOnNextDelayError( + Flowable.range(1, 10) + .map(v -> { + if (v % 2 == 0) { + return Completable.fromRunnable(run); + } + return Completable.complete(); + }) + ) + .test() + .assertResult(); + + verify(run, times(5)).run(); + } + + @Test + public void noDelaySwitch() { + PublishProcessor pp = PublishProcessor.create(); + + TestObserver to = Completable.switchOnNext(pp).test(); + + assertTrue(pp.hasSubscribers()); + + to.assertEmpty(); + + CompletableSubject cs1 = CompletableSubject.create(); + CompletableSubject cs2 = CompletableSubject.create(); + + pp.onNext(cs1); + + assertTrue(cs1.hasObservers()); + + pp.onNext(cs2); + + assertFalse(cs1.hasObservers()); + assertTrue(cs2.hasObservers()); + + pp.onComplete(); + + assertTrue(cs2.hasObservers()); + + cs2.onComplete(); + + to.assertResult(); + } + + @Test + public void delaySwitch() { + PublishProcessor pp = PublishProcessor.create(); + + TestObserver to = Completable.switchOnNextDelayError(pp).test(); + + assertTrue(pp.hasSubscribers()); + + to.assertEmpty(); + + CompletableSubject cs1 = CompletableSubject.create(); + CompletableSubject cs2 = CompletableSubject.create(); + + pp.onNext(cs1); + + assertTrue(cs1.hasObservers()); + + pp.onNext(cs2); + + assertFalse(cs1.hasObservers()); + assertTrue(cs2.hasObservers()); + + assertTrue(cs2.hasObservers()); + + cs2.onError(new TestException()); + + assertTrue(pp.hasSubscribers()); + + to.assertEmpty(); + + pp.onComplete(); + + to.assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilTest.java index b8b48fe9c0..1eefff1956 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTakeUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -145,7 +145,7 @@ public void mainErrorLate() { new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException()); } }.takeUntil(Completable.complete()) @@ -167,7 +167,7 @@ public void mainCompleteLate() { new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }.takeUntil(Completable.complete()) @@ -186,13 +186,13 @@ public void otherErrorLate() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference ref = new AtomicReference(); + final AtomicReference ref = new AtomicReference<>(); Completable.complete() .takeUntil(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }) @@ -213,13 +213,13 @@ public void otherCompleteLate() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference ref = new AtomicReference(); + final AtomicReference ref = new AtomicReference<>(); Completable.complete() .takeUntil(new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeoutTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeoutTest.java index 9261562220..86f2f2890a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeoutTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimeoutTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -155,8 +155,8 @@ public void run() { @Test public void ambRace() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); CompositeDisposable cd = new CompositeDisposable(); AtomicBoolean once = new AtomicBoolean(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimerTest.java index 7f6b339961..bfd87a1af6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public void dispose() { public void timerInterruptible() throws Exception { ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); try { - for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec) }) { + for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec, true) }) { final AtomicBoolean interrupted = new AtomicBoolean(); TestObserver to = Completable.timer(1, TimeUnit.MILLISECONDS, s) .doOnComplete(new Action() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowableTest.java index d1fdb548fa..8c10ea5225 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFutureTest.java new file mode 100644 index 0000000000..c857e95ebb --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToFutureTest.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.completable; + +import static org.junit.Assert.*; + +import java.util.concurrent.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.CompletableSubject; + +public class CompletableToFutureTest extends RxJavaTest { + + @Test + public void empty() throws Exception { + assertNull(Completable.complete() + .subscribeOn(Schedulers.computation()) + .toFuture() + .get()); + } + + @Test + public void error() throws InterruptedException { + try { + Completable.error(new TestException()) + .subscribeOn(Schedulers.computation()) + .toFuture() + .get(); + + fail("Should have thrown!"); + } catch (ExecutionException ex) { + assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException); + } + } + + @Test + public void cancel() { + CompletableSubject cs = CompletableSubject.create(); + + Future f = cs.toFuture(); + + assertTrue(cs.hasObservers()); + + f.cancel(true); + + assertFalse(cs.hasObservers()); + } + + @Test + public void cancel2() { + CompletableSubject cs = CompletableSubject.create(); + + Future f = cs.toFuture(); + + assertTrue(cs.hasObservers()); + + f.cancel(false); + + assertFalse(cs.hasObservers()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservableTest.java index d94a4ba097..7ad93e9bef 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableToObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,16 +13,10 @@ package io.reactivex.rxjava3.internal.operators.completable; -import static org.junit.Assert.*; - import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; -import io.reactivex.rxjava3.internal.operators.completable.CompletableToObservable.ObserverCompletableObserver; -import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.testsupport.TestHelper; public class CompletableToObservableTest extends RxJavaTest { @@ -37,36 +31,4 @@ public Observable apply(Completable c) throws Exception { }); } - @Test - public void fusion() throws Exception { - TestObserver to = new TestObserver(); - - ObserverCompletableObserver co = new ObserverCompletableObserver(to); - - Disposable d = Disposables.empty(); - - co.onSubscribe(d); - - assertEquals(QueueFuseable.NONE, co.requestFusion(QueueFuseable.SYNC)); - - assertEquals(QueueFuseable.ASYNC, co.requestFusion(QueueFuseable.ASYNC)); - - assertEquals(QueueFuseable.ASYNC, co.requestFusion(QueueFuseable.ANY)); - - assertTrue(co.isEmpty()); - - assertNull(co.poll()); - - co.clear(); - - assertFalse(co.isDisposed()); - - co.dispose(); - - assertTrue(d.isDisposed()); - - assertTrue(co.isDisposed()); - - TestHelper.assertNoOffer(co); - } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUnsafeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUnsafeTest.java index 7cad5802b6..72b28edea0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUnsafeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUnsafeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -42,7 +42,7 @@ public void wrapCustomCompletable() { Completable.wrap(new CompletableSource() { @Override public void subscribe(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsingTest.java index ca4634c7f5..72e4d16491 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/completable/CompletableUsingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -411,11 +411,11 @@ public CompletableSource apply(Object v) throws Exception { return Completable.wrap(new CompletableSource() { @Override public void subscribe(CompletableObserver observer) { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstreamTest.java index e109a0a544..65f593211c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstreamTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstreamTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatestTest.java index 825c74e321..a842c39c2b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecentTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecentTest.java index f652e9fbf6..92513b3072 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecentTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableMostRecentTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,10 +26,6 @@ import io.reactivex.rxjava3.schedulers.TestScheduler; public class BlockingFlowableMostRecentTest extends RxJavaTest { - @Test - public void mostRecentNull() { - assertNull(Flowable.never().blockingMostRecent(null).iterator().next()); - } @Test public void mostRecent() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java index 8bc31fa85e..718a7ba6a1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableNextTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -223,8 +223,8 @@ public void nextWithCallingHasNextMultipleTimes() { /** * Confirm that no buffering or blocking of the Observable onNext calls occurs and it just grabs the next emitted value. - *

    - * This results in output such as => a: 1 b: 2 c: 89 + *

    + * This results in output such as {@code => a: 1 b: 2 c: 89} * * @throws Throwable some method call is declared throws */ @@ -352,7 +352,7 @@ public void interrupt() { @Test public void nextObserverError() { - NextSubscriber no = new NextSubscriber(); + NextSubscriber no = new NextSubscriber<>(); List errors = TestHelper.trackPluginErrors(); try { @@ -366,7 +366,7 @@ public void nextObserverError() { @Test public void nextObserverOnNext() throws Exception { - NextSubscriber no = new NextSubscriber(); + NextSubscriber no = new NextSubscriber<>(); no.setWaiting(); no.onNext(Notification.createOnNext(1)); @@ -379,7 +379,7 @@ public void nextObserverOnNext() throws Exception { @Test public void nextObserverOnCompleteOnNext() throws Exception { - NextSubscriber no = new NextSubscriber(); + NextSubscriber no = new NextSubscriber<>(); no.setWaiting(); no.onNext(Notification.createOnComplete()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToFutureTest.java index cf8ad48a5f..6da74eea03 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToFutureTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToFutureTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.flowable; import static org.junit.Assert.*; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToIteratorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToIteratorTest.java index 20bfcf9962..e5ad7806d3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToIteratorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BlockingFlowableToIteratorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -117,13 +117,13 @@ public void remove() { @Test(expected = UnsupportedOperationException.class) public void remove() { - BlockingFlowableIterator it = new BlockingFlowableIterator(128); + BlockingFlowableIterator it = new BlockingFlowableIterator<>(128); it.remove(); } @Test public void dispose() { - BlockingFlowableIterator it = new BlockingFlowableIterator(128); + BlockingFlowableIterator it = new BlockingFlowableIterator<>(128); assertFalse(it.isDisposed()); @@ -134,7 +134,7 @@ public void dispose() { @Test public void interruptWait() { - BlockingFlowableIterator it = new BlockingFlowableIterator(128); + BlockingFlowableIterator it = new BlockingFlowableIterator<>(128); try { Thread.currentThread().interrupt(); @@ -147,12 +147,12 @@ public void interruptWait() { @Test(expected = NoSuchElementException.class) public void emptyThrowsNoSuch() { - BlockingFlowableIterator it = new BlockingFlowableIterator(128); + BlockingFlowableIterator it = new BlockingFlowableIterator<>(128); it.onComplete(); it.next(); } - @Test(expected = MissingBackpressureException.class) + @Test(expected = QueueOverflowException.class) public void overflowQueue() { Iterator it = new Flowable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BufferUntilSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BufferUntilSubscriberTest.java index 69abd706eb..6cdc17d210 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BufferUntilSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/BufferUntilSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllTest.java index 7515b66cb0..2b66a48bd0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAllTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -146,7 +146,7 @@ public Publisher apply(Boolean t1) { @Test public void backpressureIfOneRequestedOneShouldBeDelivered() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Flowable.empty().all(new Predicate() { @Override @@ -164,7 +164,7 @@ public boolean test(Object t) { @Test public void predicateThrowsExceptionAndValueInCauseMessage() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); @@ -306,7 +306,7 @@ public Publisher apply(Boolean t1) { @Test public void backpressureIfNoneRequestedNoneShouldBeDeliveredFlowable() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.empty().all(new Predicate() { @Override public boolean test(Object t1) { @@ -323,7 +323,7 @@ public boolean test(Object t1) { @Test public void backpressureIfOneRequestedOneShouldBeDeliveredFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(1L); + TestSubscriberEx ts = new TestSubscriberEx<>(1L); Flowable.empty().all(new Predicate() { @Override @@ -343,7 +343,7 @@ public boolean test(Object t) { @Test public void predicateThrowsExceptionAndValueInCauseMessageFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java index 72073f3150..1a1ceca926 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAmbTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,6 +25,7 @@ import org.mockito.InOrder; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.exceptions.TestException; @@ -102,7 +103,6 @@ public void amb() { Flowable flowable3 = createFlowable(new String[] { "3", "33", "333", "3333" }, 3000, null); - @SuppressWarnings("unchecked") Flowable f = Flowable.ambArray(flowable1, flowable2, flowable3); @@ -131,7 +131,6 @@ public void amb2() { Flowable flowable3 = createFlowable(new String[] {}, 3000, new IOException("fake exception")); - @SuppressWarnings("unchecked") Flowable f = Flowable.ambArray(flowable1, flowable2, flowable3); @@ -158,7 +157,6 @@ public void amb3() { Flowable flowable3 = createFlowable(new String[] { "3" }, 3000, null); - @SuppressWarnings("unchecked") Flowable f = Flowable.ambArray(flowable1, flowable2, flowable3); @@ -171,10 +169,9 @@ public void amb3() { inOrder.verifyNoMoreInteractions(); } - @SuppressWarnings("unchecked") @Test public void producerRequestThroughAmb() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(3); final AtomicLong requested1 = new AtomicLong(); final AtomicLong requested2 = new AtomicLong(); @@ -225,7 +222,7 @@ public void cancel() { @Test public void backpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, Flowable.bufferSize() * 2) .ambWith(Flowable.range(0, Flowable.bufferSize() * 2)) .observeOn(Schedulers.computation()) // observeOn has a backpressured RxRingBuffer @@ -237,7 +234,6 @@ public void backpressure() { assertEquals(Flowable.bufferSize() * 2, ts.values().size()); } - @SuppressWarnings("unchecked") @Test public void subscriptionOnlyHappensOnce() throws InterruptedException { final AtomicLong count = new AtomicLong(); @@ -254,7 +250,7 @@ public void accept(Subscription s) { //this stream emits second Flowable f2 = Flowable.just(1).doOnSubscribe(incrementer) .delay(100, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.computation()); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.ambArray(f1, f2).subscribe(ts); ts.request(1); ts.awaitDone(5, TimeUnit.SECONDS); @@ -262,7 +258,6 @@ public void accept(Subscription s) { assertEquals(2, count.get()); } - @SuppressWarnings("unchecked") @Test public void secondaryRequestsPropagatedToChildren() throws InterruptedException { //this aync stream should emit first @@ -271,7 +266,7 @@ public void secondaryRequestsPropagatedToChildren() throws InterruptedException //this stream emits second Flowable f2 = Flowable.fromArray(4, 5, 6) .delay(200, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.computation()); - TestSubscriber ts = new TestSubscriber(1L); + TestSubscriber ts = new TestSubscriber<>(1L); Flowable.ambArray(f1, f2).subscribe(ts); // before first emission request 20 more @@ -302,14 +297,13 @@ public void accept(Integer t) { assertEquals(1, result); } - @SuppressWarnings("unchecked") @Test public void ambCancelsOthers() { PublishProcessor source1 = PublishProcessor.create(); PublishProcessor source2 = PublishProcessor.create(); PublishProcessor source3 = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.ambArray(source1, source2, source3).subscribe(ts); @@ -327,8 +321,8 @@ public void ambCancelsOthers() { @Test public void multipleUse() { - TestSubscriber ts1 = new TestSubscriber(); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber<>(); Flowable amb = Flowable.timer(100, TimeUnit.MILLISECONDS).ambWith(Flowable.timer(200, TimeUnit.MILLISECONDS)); @@ -347,7 +341,6 @@ public void multipleUse() { ts2.assertNoErrors(); } - @SuppressWarnings("unchecked") @Test public void ambIterable() { PublishProcessor pp1 = PublishProcessor.create(); @@ -370,7 +363,6 @@ public void ambIterable() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void ambIterable2() { PublishProcessor pp1 = PublishProcessor.create(); @@ -393,19 +385,16 @@ public void ambIterable2() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void ambArrayEmpty() { assertSame(Flowable.empty(), Flowable.ambArray()); } - @SuppressWarnings("unchecked") @Test public void ambArraySingleElement() { assertSame(Flowable.never(), Flowable.ambArray(Flowable.never())); } - @SuppressWarnings("unchecked") @Test public void disposed() { TestHelper.checkDisposed(Flowable.ambArray(Flowable.never(), Flowable.never())); @@ -413,7 +402,8 @@ public void disposed() { @Test public void manySources() { - Flowable[] a = new Flowable[32]; + @SuppressWarnings("unchecked") + Flowable[] a = new Flowable[32]; Arrays.fill(a, Flowable.never()); a[31] = Flowable.just(1); @@ -442,7 +432,6 @@ public void onNextRace() { final PublishProcessor pp1 = PublishProcessor.create(); final PublishProcessor pp2 = PublishProcessor.create(); - @SuppressWarnings("unchecked") TestSubscriberEx ts = Flowable.ambArray(pp1, pp2).to(TestHelper.testConsumer()); Runnable r1 = new Runnable() { @@ -471,7 +460,6 @@ public void onCompleteRace() { final PublishProcessor pp1 = PublishProcessor.create(); final PublishProcessor pp2 = PublishProcessor.create(); - @SuppressWarnings("unchecked") TestSubscriber ts = Flowable.ambArray(pp1, pp2).test(); Runnable r1 = new Runnable() { @@ -499,7 +487,6 @@ public void onErrorRace() { final PublishProcessor pp1 = PublishProcessor.create(); final PublishProcessor pp2 = PublishProcessor.create(); - @SuppressWarnings("unchecked") TestSubscriber ts = Flowable.ambArray(pp1, pp2).test(); final Throwable ex = new TestException(); @@ -531,7 +518,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void nullIterableElement() { Flowable.amb(Arrays.asList(Flowable.never(), null, Flowable.never())) @@ -541,7 +527,7 @@ public void nullIterableElement() { @Test public void iteratorThrows() { - Flowable.amb(new CrashingMappedIterable>(1, 100, 100, new Function>() { + Flowable.amb(new CrashingMappedIterable<>(1, 100, 100, new Function>() { @Override public Flowable apply(Integer v) throws Exception { return Flowable.never(); @@ -550,7 +536,7 @@ public Flowable apply(Integer v) throws Exception { .to(TestHelper.testConsumer()) .assertFailureAndMessage(TestException.class, "iterator()"); - Flowable.amb(new CrashingMappedIterable>(100, 1, 100, new Function>() { + Flowable.amb(new CrashingMappedIterable<>(100, 1, 100, new Function>() { @Override public Flowable apply(Integer v) throws Exception { return Flowable.never(); @@ -559,7 +545,7 @@ public Flowable apply(Integer v) throws Exception { .to(TestHelper.testConsumer()) .assertFailureAndMessage(TestException.class, "hasNext()"); - Flowable.amb(new CrashingMappedIterable>(100, 100, 1, new Function>() { + Flowable.amb(new CrashingMappedIterable<>(100, 100, 1, new Function>() { @Override public Flowable apply(Integer v) throws Exception { return Flowable.never(); @@ -575,21 +561,18 @@ public void ambWithOrder() { Flowable.just(1).ambWith(error).test().assertValue(1).assertComplete(); } - @SuppressWarnings("unchecked") @Test public void ambIterableOrder() { Flowable error = Flowable.error(new RuntimeException()); Flowable.amb(Arrays.asList(Flowable.just(1), error)).test().assertValue(1).assertComplete(); } - @SuppressWarnings("unchecked") @Test public void ambArrayOrder() { Flowable error = Flowable.error(new RuntimeException()); Flowable.ambArray(Flowable.just(1), error).test().assertValue(1).assertComplete(); } - @SuppressWarnings("unchecked") @Test public void noWinnerSuccessDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -615,7 +598,6 @@ public void accept(Object v) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerErrorDispose() throws Exception { final TestException ex = new TestException(); @@ -642,7 +624,6 @@ public void accept(Throwable e) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerCompleteDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -668,7 +649,6 @@ public void run() throws Exception { } } - @SuppressWarnings("unchecked") @Test public void publishersInIterable() { Publisher source = new Publisher() { @@ -682,4 +662,34 @@ public void subscribe(Subscriber subscriber) { .test() .assertResult(1); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.amb(Arrays.asList(Flowable.never(), Flowable.never()))); + } + + @Test + public void requestAfterCancel() { + Flowable.amb(Arrays.asList(Flowable.never(), Flowable.never())) + .subscribe(new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Object t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + s.cancel(); + s.request(1); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnyTest.java index 936d74f5e0..2f18a90c64 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAnyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -239,7 +239,7 @@ public Publisher apply(Boolean t1) { @Test public void backpressureIfOneRequestedOneShouldBeDelivered() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Flowable.just(1).any(new Predicate() { @Override public boolean test(Integer v) { @@ -255,7 +255,7 @@ public boolean test(Integer v) { @Test public void predicateThrowsExceptionAndValueInCauseMessage() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); Flowable.just("Boo!").any(new Predicate() { @@ -488,7 +488,7 @@ public Publisher apply(Boolean t1) { @Test public void backpressureIfNoneRequestedNoneShouldBeDeliveredFlowable() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.just(1).any(new Predicate() { @Override @@ -505,7 +505,7 @@ public boolean test(Integer t) { @Test public void backpressureIfOneRequestedOneShouldBeDeliveredFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(1L); + TestSubscriberEx ts = new TestSubscriberEx<>(1L); Flowable.just(1).any(new Predicate() { @Override public boolean test(Integer v) { @@ -521,7 +521,7 @@ public boolean test(Integer v) { @Test public void predicateThrowsExceptionAndValueInCauseMessageFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); Flowable.just("Boo!").any(new Predicate() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAsObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAsObservableTest.java index 15cd39ea0a..d922ef48c3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAsObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAsObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnectTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnectTest.java index d2213dd911..07d1e66214 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnectTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableAutoConnectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingTest.java index cb767ef718..a1facf4046 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBlockingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -49,7 +49,7 @@ public void blockingFirstDefault() { @Test public void blockingSubscribeConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -65,7 +65,7 @@ public void accept(Integer v) throws Exception { @Test public void boundedBlockingSubscribeConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -81,7 +81,7 @@ public void accept(Integer v) throws Exception { @Test public void boundedBlockingSubscribeConsumerBufferExceed() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -97,7 +97,7 @@ public void accept(Integer v) throws Exception { @Test public void blockingSubscribeConsumerConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -113,7 +113,7 @@ public void accept(Integer v) throws Exception { @Test public void boundedBlockingSubscribeConsumerConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -129,7 +129,7 @@ public void accept(Integer v) throws Exception { @Test public void boundedBlockingSubscribeConsumerConsumerBufferExceed() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -145,7 +145,7 @@ public void accept(Integer v) throws Exception { @Test public void blockingSubscribeConsumerConsumerError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); TestException ex = new TestException(); @@ -165,7 +165,7 @@ public void accept(Object v) throws Exception { @Test public void boundedBlockingSubscribeConsumerConsumerError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); TestException ex = new TestException(); @@ -185,7 +185,7 @@ public void accept(Object v) throws Exception { @Test public void blockingSubscribeConsumerConsumerAction() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Consumer cons = new Consumer() { @Override @@ -208,7 +208,7 @@ public void run() throws Exception { @Test public void boundedBlockingSubscribeConsumerConsumerAction() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Consumer cons = new Consumer() { @Override @@ -233,7 +233,7 @@ public void run() throws Exception { @Test public void boundedBlockingSubscribeConsumerConsumerActionBufferExceed() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Consumer cons = new Consumer() { @Override @@ -258,7 +258,7 @@ public void run() throws Exception { @Test public void boundedBlockingSubscribeConsumerConsumerActionBufferExceedMillionItem() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Consumer cons = new Consumer() { @Override @@ -283,7 +283,7 @@ public void run() throws Exception { @Test public void blockingSubscribeObserver() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -316,7 +316,7 @@ public void onComplete() { @Test public void blockingSubscribeObserverError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); final TestException ex = new TestException(); @@ -430,7 +430,7 @@ public void subscribe(Subscriber s) { @Test public void interrupt() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Thread.currentThread().interrupt(); @@ -451,7 +451,7 @@ public void blockingSingleEmpty() { @Test public void onCompleteDelayed() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.empty().delay(100, TimeUnit.MILLISECONDS) .blockingSubscribe(ts); @@ -466,7 +466,7 @@ public void utilityClass() { @Test public void disposeUpFront() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.cancel(); Flowable.just(1).blockingSubscribe(ts); @@ -476,7 +476,7 @@ public void disposeUpFront() { @SuppressWarnings("rawtypes") @Test public void delayed() throws Exception { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final Subscriber[] s = { null }; Schedulers.single().scheduleDirect(new Runnable() { @@ -506,7 +506,7 @@ protected void subscribeActual(Subscriber subscriber) { @Test public void blockinsSubscribeCancelAsync() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final PublishProcessor pp = PublishProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java index 781de93fb0..c1b08990bb 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableBufferTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ import io.reactivex.rxjava3.internal.operators.flowable.FlowableBufferTimed.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -272,7 +272,7 @@ public void accept(List t1) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -302,7 +302,7 @@ public void bufferStopsWhenUnsubscribed1() { Flowable source = Flowable.never(); Subscriber> subscriber = TestHelper.mockSubscriber(); - TestSubscriber> ts = new TestSubscriber>(subscriber, 0L); + TestSubscriber> ts = new TestSubscriber<>(subscriber, 0L); source.buffer(100, 200, TimeUnit.MILLISECONDS, scheduler) .doOnNext(new Consumer>() { @@ -730,7 +730,7 @@ public Flowable apply(Integer t1) { @Test public void producerRequestThroughBufferWithSize1() { - TestSubscriber> ts = new TestSubscriber>(3L); + TestSubscriber> ts = new TestSubscriber<>(3L); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -761,7 +761,7 @@ public void cancel() { @Test public void producerRequestThroughBufferWithSize2() { - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -789,7 +789,7 @@ public void cancel() { @Test public void producerRequestThroughBufferWithSize3() { - TestSubscriber> ts = new TestSubscriber>(3L); + TestSubscriber> ts = new TestSubscriber<>(3L); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -818,7 +818,7 @@ public void cancel() { @Test public void producerRequestThroughBufferWithSize4() { - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -845,7 +845,7 @@ public void cancel() { @Test public void producerRequestOverflowThroughBufferWithSize1() { - TestSubscriber> ts = new TestSubscriber>(Long.MAX_VALUE >> 1); + TestSubscriber> ts = new TestSubscriber<>(Long.MAX_VALUE >> 1); final AtomicLong requested = new AtomicLong(); @@ -874,7 +874,7 @@ public void cancel() { @Test public void producerRequestOverflowThroughBufferWithSize2() { - TestSubscriber> ts = new TestSubscriber>(Long.MAX_VALUE >> 1); + TestSubscriber> ts = new TestSubscriber<>(Long.MAX_VALUE >> 1); final AtomicLong requested = new AtomicLong(); @@ -988,7 +988,6 @@ public void onComplete() { assertFalse(s.isDisposed()); } - @SuppressWarnings("unchecked") @Test public void postCompleteBackpressure() { Flowable> source = Flowable.range(1, 10).buffer(3, 1); @@ -1064,7 +1063,6 @@ public void postCompleteBackpressure() { ts.assertNoErrors(); } - @SuppressWarnings("unchecked") @Test public void timeAndSkipOverlap() { @@ -1104,7 +1102,6 @@ public void timeAndSkipOverlap() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void timeAndSkipSkip() { @@ -1141,7 +1138,6 @@ public void timeAndSkipSkip() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void timeAndSkipOverlapScheduler() { @@ -1192,7 +1188,6 @@ public Scheduler apply(Scheduler t) { } } - @SuppressWarnings("unchecked") @Test public void timeAndSkipSkipDefaultScheduler() { RxJavaPlugins.setComputationSchedulerHandler(new Function() { @@ -1240,7 +1235,6 @@ public Scheduler apply(Scheduler t) { } } - @SuppressWarnings("unchecked") @Test public void bufferBoundaryHint() { Flowable.range(1, 5).buffer(Flowable.timer(1, TimeUnit.MINUTES), 2) @@ -1249,38 +1243,35 @@ public void bufferBoundaryHint() { } static HashSet set(Integer... values) { - return new HashSet(Arrays.asList(values)); + return new HashSet<>(Arrays.asList(values)); } - @SuppressWarnings("unchecked") @Test public void bufferIntoCustomCollection() { Flowable.just(1, 1, 2, 2, 3, 3, 4, 4) .buffer(3, new Supplier>() { @Override public Collection get() throws Exception { - return new HashSet(); + return new HashSet<>(); } }) .test() .assertResult(set(1, 2), set(2, 3), set(4)); } - @SuppressWarnings("unchecked") @Test public void bufferSkipIntoCustomCollection() { Flowable.just(1, 1, 2, 2, 3, 3, 4, 4) .buffer(3, 3, new Supplier>() { @Override public Collection get() throws Exception { - return new HashSet(); + return new HashSet<>(); } }) .test() .assertResult(set(1, 2), set(2, 3), set(4)); } - @SuppressWarnings("unchecked") @Test public void bufferTimeSkipDefault() { Flowable.range(1, 5).buffer(1, 1, TimeUnit.MINUTES) @@ -1307,7 +1298,6 @@ public void dispose() { } @Test - @SuppressWarnings("unchecked") public void supplierReturnsNull() { Flowable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), Integer.MAX_VALUE, new Supplier>() { @@ -1317,7 +1307,7 @@ public Collection get() throws Exception { if (count++ == 1) { return null; } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -1327,7 +1317,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierReturnsNull2() { Flowable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), 10, new Supplier>() { @@ -1337,7 +1326,7 @@ public Collection get() throws Exception { if (count++ == 1) { return null; } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -1347,7 +1336,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierReturnsNull3() { Flowable.never() .buffer(2, 1, TimeUnit.MILLISECONDS, Schedulers.single(), new Supplier>() { @@ -1357,7 +1345,7 @@ public Collection get() throws Exception { if (count++ == 1) { return null; } else { - return new ArrayList(); + return new ArrayList<>(); } } }) @@ -1367,7 +1355,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows() { Flowable.just(1) .buffer(1, TimeUnit.SECONDS, Schedulers.single(), Integer.MAX_VALUE, new Supplier>() { @@ -1381,7 +1368,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows2() { Flowable.just(1) .buffer(1, TimeUnit.SECONDS, Schedulers.single(), 10, new Supplier>() { @@ -1395,7 +1381,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows3() { Flowable.just(1) .buffer(2, 1, TimeUnit.SECONDS, Schedulers.single(), new Supplier>() { @@ -1409,7 +1394,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows4() { Flowable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), Integer.MAX_VALUE, new Supplier>() { @@ -1419,7 +1403,7 @@ public Collection get() throws Exception { if (count++ == 1) { throw new TestException(); } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -1429,7 +1413,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows5() { Flowable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), 10, new Supplier>() { @@ -1439,7 +1422,7 @@ public Collection get() throws Exception { if (count++ == 1) { throw new TestException(); } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -1449,7 +1432,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows6() { Flowable.never() .buffer(2, 1, TimeUnit.MILLISECONDS, Schedulers.single(), new Supplier>() { @@ -1459,7 +1441,7 @@ public Collection get() throws Exception { if (count++ == 1) { throw new TestException(); } else { - return new ArrayList(); + return new ArrayList<>(); } } }) @@ -1468,7 +1450,6 @@ public Collection get() throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void restartTimer() { Flowable.range(1, 5) @@ -1477,7 +1458,6 @@ public void restartTimer() { .assertResult(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5)); } - @SuppressWarnings("unchecked") @Test public void bufferSkipError() { Flowable.error(new TestException()) @@ -1486,7 +1466,6 @@ public void bufferSkipError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferSupplierCrash2() { Flowable.range(1, 2) @@ -1497,14 +1476,13 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }) .test() .assertFailure(TestException.class, Arrays.asList(1)); } - @SuppressWarnings("unchecked") @Test public void bufferSkipSupplierCrash2() { Flowable.range(1, 2) @@ -1515,14 +1493,13 @@ public List get() throws Exception { if (++calls == 1) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }) .test() .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferOverlapSupplierCrash2() { Flowable.range(1, 2) @@ -1533,14 +1510,13 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }) .test() .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferSkipOverlap() { Flowable.range(1, 5) @@ -1555,7 +1531,6 @@ public void bufferSkipOverlap() { ); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactError() { Flowable.error(new TestException()) @@ -1564,7 +1539,6 @@ public void bufferTimedExactError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferTimedSkipError() { Flowable.error(new TestException()) @@ -1573,7 +1547,6 @@ public void bufferTimedSkipError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferTimedOverlapError() { Flowable.error(new TestException()) @@ -1582,7 +1555,6 @@ public void bufferTimedOverlapError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactEmpty() { Flowable.empty() @@ -1591,7 +1563,6 @@ public void bufferTimedExactEmpty() { .assertResult(Collections.emptyList()); } - @SuppressWarnings("unchecked") @Test public void bufferTimedSkipEmpty() { Flowable.empty() @@ -1600,7 +1571,6 @@ public void bufferTimedSkipEmpty() { .assertResult(Collections.emptyList()); } - @SuppressWarnings("unchecked") @Test public void bufferTimedOverlapEmpty() { Flowable.empty() @@ -1609,7 +1579,6 @@ public void bufferTimedOverlapEmpty() { .assertResult(Collections.emptyList()); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactSupplierCrash() { TestScheduler scheduler = new TestScheduler(); @@ -1624,7 +1593,7 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }, true) .test(); @@ -1639,7 +1608,6 @@ public List get() throws Exception { .assertFailure(TestException.class, Arrays.asList(1)); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactBoundedError() { TestScheduler scheduler = new TestScheduler(); @@ -1713,7 +1681,6 @@ public void badRequest() { TestHelper.assertBadRequestReported(PublishProcessor.create().buffer(2, 1)); } - @SuppressWarnings("unchecked") @Test public void skipError() { Flowable.error(new TestException()) @@ -1722,7 +1689,6 @@ public void skipError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void skipSingleResult() { Flowable.just(1) @@ -1731,7 +1697,6 @@ public void skipSingleResult() { .assertResult(Arrays.asList(1)); } - @SuppressWarnings("unchecked") @Test public void skipBackpressure() { Flowable.range(1, 10) @@ -1782,7 +1747,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void noCompletionCancelExact() { final AtomicInteger counter = new AtomicInteger(); @@ -1802,7 +1766,6 @@ public void run() throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noCompletionCancelSkip() { final AtomicInteger counter = new AtomicInteger(); @@ -1822,7 +1785,6 @@ public void run() throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noCompletionCancelOverlap() { final AtomicInteger counter = new AtomicInteger(); @@ -1843,7 +1805,6 @@ public void run() throws Exception { } @Test - @SuppressWarnings("unchecked") public void boundaryOpenCloseDisposedOnComplete() { PublishProcessor source = PublishProcessor.create(); @@ -1919,7 +1880,6 @@ public Publisher apply(Long a) { } @Test - @SuppressWarnings("unchecked") public void openClosemainError() { Flowable.error(new TestException()) .buffer(Flowable.never(), Functions.justFunction(Flowable.never())) @@ -1928,7 +1888,6 @@ public void openClosemainError() { } @Test - @SuppressWarnings("unchecked") public void openClosebadSource() { List errors = TestHelper.trackPluginErrors(); try { @@ -1966,7 +1925,6 @@ protected void subscribeActual(Subscriber s) { } @Test - @SuppressWarnings("unchecked") public void openCloseOpenCompletes() { PublishProcessor source = PublishProcessor.create(); @@ -1995,7 +1953,6 @@ public void openCloseOpenCompletes() { } @Test - @SuppressWarnings("unchecked") public void openCloseOpenCompletesNoBuffers() { PublishProcessor source = PublishProcessor.create(); @@ -2024,7 +1981,6 @@ public void openCloseOpenCompletesNoBuffers() { } @Test - @SuppressWarnings("unchecked") public void openCloseTake() { PublishProcessor source = PublishProcessor.create(); @@ -2048,7 +2004,6 @@ public void openCloseTake() { } @Test - @SuppressWarnings("unchecked") public void openCloseEmptyBackpressure() { PublishProcessor source = PublishProcessor.create(); @@ -2069,7 +2024,6 @@ public void openCloseEmptyBackpressure() { } @Test - @SuppressWarnings("unchecked") public void openCloseErrorBackpressure() { PublishProcessor source = PublishProcessor.create(); @@ -2090,7 +2044,6 @@ public void openCloseErrorBackpressure() { } @Test - @SuppressWarnings("unchecked") public void openCloseBadOpen() { List errors = TestHelper.trackPluginErrors(); try { @@ -2134,7 +2087,6 @@ protected void subscribeActual(Subscriber s) { } @Test - @SuppressWarnings("unchecked") public void openCloseBadClose() { List errors = TestHelper.trackPluginErrors(); try { @@ -2191,7 +2143,6 @@ public Flowable> apply(Flowable f) ); } - @SuppressWarnings("unchecked") @Test public void bufferExactBoundarySecondBufferCrash() { PublishProcessor pp = PublishProcessor.create(); @@ -2204,7 +2155,7 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }).test(); @@ -2213,7 +2164,6 @@ public List get() throws Exception { ts.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferExactBoundaryBadSource() { Flowable pp = new Flowable() { @@ -2226,7 +2176,7 @@ protected void subscribeActual(Subscriber subscriber) { } }; - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); Flowable b = new Flowable() { @Override protected void subscribeActual(Subscriber subscriber) { @@ -2303,10 +2253,10 @@ public void timedCancelledUpfront() { public void timedInternalState() { TestScheduler sch = new TestScheduler(); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); - BufferExactUnboundedSubscriber> sub = new BufferExactUnboundedSubscriber>( - ts, Functions.justSupplier((List)new ArrayList()), 1, TimeUnit.SECONDS, sch); + BufferExactUnboundedSubscriber> sub = new BufferExactUnboundedSubscriber<>( + ts, Functions.justSupplier((List) new ArrayList()), 1, TimeUnit.SECONDS, sch); sub.onSubscribe(new BooleanSubscription()); @@ -2322,7 +2272,7 @@ public void timedInternalState() { assertTrue(sub.isDisposed()); - sub.buffer = new ArrayList(); + sub.buffer = new ArrayList<>(); sub.enter(); sub.onComplete(); } @@ -2353,10 +2303,10 @@ public Publisher> apply(Flowable f) public void timedSkipInternalState() { TestScheduler sch = new TestScheduler(); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); - BufferSkipBoundedSubscriber> sub = new BufferSkipBoundedSubscriber>( - ts, Functions.justSupplier((List)new ArrayList()), 1, 1, TimeUnit.SECONDS, sch.createWorker()); + BufferSkipBoundedSubscriber> sub = new BufferSkipBoundedSubscriber<>( + ts, Functions.justSupplier((List) new ArrayList()), 1, 1, TimeUnit.SECONDS, sch.createWorker()); sub.onSubscribe(new BooleanSubscription()); @@ -2372,19 +2322,20 @@ public void timedSkipInternalState() { public void timedSkipCancelWhenSecondBuffer() { TestScheduler sch = new TestScheduler(); - final TestSubscriber> ts = new TestSubscriber>(); + final TestSubscriber> ts = new TestSubscriber<>(); - BufferSkipBoundedSubscriber> sub = new BufferSkipBoundedSubscriber>( + BufferSkipBoundedSubscriber> sub = new BufferSkipBoundedSubscriber<>( ts, new Supplier>() { - int calls; - @Override - public List get() throws Exception { - if (++calls == 2) { - ts.cancel(); - } - return new ArrayList(); - } - }, 1, 1, TimeUnit.SECONDS, sch.createWorker()); + int calls; + + @Override + public List get() throws Exception { + if (++calls == 2) { + ts.cancel(); + } + return new ArrayList<>(); + } + }, 1, 1, TimeUnit.SECONDS, sch.createWorker()); sub.onSubscribe(new BooleanSubscription()); @@ -2397,11 +2348,11 @@ public List get() throws Exception { public void timedSizeBufferAlreadyCleared() { TestScheduler sch = new TestScheduler(); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); BufferExactBoundedSubscriber> sub = - new BufferExactBoundedSubscriber>( - ts, Functions.justSupplier((List)new ArrayList()), + new BufferExactBoundedSubscriber<>( + ts, Functions.justSupplier((List) new ArrayList()), 1, TimeUnit.SECONDS, 1, false, sch.createWorker()) ; @@ -2424,7 +2375,6 @@ public void timedSizeBufferAlreadyCleared() { } @Test - @SuppressWarnings("unchecked") public void bufferExactFailingSupplier() { Flowable.empty() .buffer(1, TimeUnit.SECONDS, Schedulers.computation(), 10, new Supplier>() { @@ -2438,4 +2388,112 @@ public List get() throws Exception { .assertFailure(TestException.class) ; } + + @Test + public void exactBadRequest() { + TestHelper.assertBadRequestReported(Flowable.never().buffer(1)); + } + + @Test + public void skipBadRequest() { + TestHelper.assertBadRequestReported(Flowable.never().buffer(1, 2)); + } + + @Test + public void overlapBadRequest() { + TestHelper.assertBadRequestReported(Flowable.never().buffer(2, 1)); + } + + @Test + public void bufferExactBoundedOnNextAfterDispose() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.unsafeCreate(s -> { + s.onSubscribe(new BooleanSubscription()); + ts.cancel(); + s.onNext(1); + }) + .buffer(1, TimeUnit.MINUTES, 2) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void boundaryCloseCompleteRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + BehaviorProcessor bp = BehaviorProcessor.createDefault(1); + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = bp + .buffer(BehaviorProcessor.createDefault(0), v -> pp) + .test(); + + TestHelper.race( + () -> bp.onComplete(), + () -> pp.onComplete() + ); + + ts.assertResult(Arrays.asList(1)); + } + } + + @Test + public void doubleOnSubscribeStartEnd() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.buffer(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void cancel() { + TestHelper.checkDisposed(Flowable.never().buffer(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void startEndCancelAfterOneBuffer() { + BehaviorProcessor.createDefault(1) + .buffer(BehaviorProcessor.createDefault(2), v -> Flowable.just(1)) + .takeUntil(v -> true) + .test() + .assertResult(Arrays.asList()); + } + + @Test + public void startEndCompleteOnBoundary() { + Flowable.empty() + .buffer(Flowable.never(), v -> Flowable.just(1)) + .take(1) + .test() + .assertResult(); + } + + @Test + public void startEndBackpressure() { + BehaviorProcessor.createDefault(1) + .buffer(BehaviorProcessor.createDefault(2), v -> Flowable.just(1)) + .test(1L) + .assertValuesOnly(Arrays.asList()); + } + + @Test + public void startEndBackpressureMoreWork() { + PublishProcessor bp = PublishProcessor.create(); + PublishProcessor pp = PublishProcessor.create(); + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber> ts = bp + .buffer(pp, v -> Flowable.just(1)) + .doOnNext(v -> { + if (counter.getAndIncrement() == 0) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(1L); + + pp.onNext(1); + bp.onNext(1); + + ts + .assertValuesOnly(Arrays.asList()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java index e13ef40f0c..e71315351d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCacheTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,11 +35,11 @@ public class FlowableCacheTest extends RxJavaTest { @Test public void coldReplayNoBackpressure() { - FlowableCache source = new FlowableCache(Flowable.range(0, 1000), 16); + FlowableCache source = new FlowableCache<>(Flowable.range(0, 1000), 16); assertFalse("Source is connected!", source.isConnected()); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.subscribe(ts); @@ -58,11 +58,11 @@ public void coldReplayNoBackpressure() { @Test public void coldReplayBackpressure() { - FlowableCache source = new FlowableCache(Flowable.range(0, 1000), 16); + FlowableCache source = new FlowableCache<>(Flowable.range(0, 1000), 16); assertFalse("Source is connected!", source.isConnected()); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(10); source.subscribe(ts); @@ -145,9 +145,9 @@ public void unsubscribeSource() throws Throwable { @Test public void take() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - FlowableCache cached = new FlowableCache(Flowable.range(1, 100), 16); + FlowableCache cached = new FlowableCache<>(Flowable.range(1, 100), 16); cached.take(10).subscribe(ts); ts.assertNoErrors(); @@ -160,9 +160,9 @@ public void take() { public void async() { Flowable source = Flowable.range(1, 10000); for (int i = 0; i < 100; i++) { - TestSubscriber ts1 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); - FlowableCache cached = new FlowableCache(source, 16); + FlowableCache cached = new FlowableCache<>(source, 16); cached.observeOn(Schedulers.computation()).subscribe(ts1); @@ -171,7 +171,7 @@ public void async() { ts1.assertComplete(); assertEquals(10000, ts1.values().size()); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts2 = new TestSubscriber<>(); cached.observeOn(Schedulers.computation()).subscribe(ts2); ts2.awaitDone(2, TimeUnit.SECONDS); @@ -186,18 +186,18 @@ public void asyncComeAndGo() { Flowable source = Flowable.interval(1, 1, TimeUnit.MILLISECONDS) .take(1000) .subscribeOn(Schedulers.io()); - FlowableCache cached = new FlowableCache(source, 16); + FlowableCache cached = new FlowableCache<>(source, 16); Flowable output = cached.observeOn(Schedulers.computation()); - List> list = new ArrayList>(100); + List> list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); list.add(ts); output.skip(i * 10).take(10).subscribe(ts); } - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 10; i++) { expected.add((long)(i - 10)); } @@ -231,7 +231,7 @@ public void subscribe(Subscriber t) { } }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); firehose.cache().observeOn(Schedulers.computation()).takeLast(100).subscribe(ts); ts.awaitDone(3, TimeUnit.SECONDS); @@ -247,14 +247,14 @@ public void valuesAndThenError() { .concatWith(Flowable.error(new TestException())) .cache(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.subscribe(ts); ts.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); ts.assertNotComplete(); ts.assertError(TestException.class); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts2 = new TestSubscriber<>(); source.subscribe(ts2); ts2.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -294,7 +294,7 @@ public void subscribeEmitRace() { cache.test(); - final TestSubscriberEx ts = new TestSubscriberEx(); + final TestSubscriberEx ts = new TestSubscriberEx<>(); Runnable r1 = new Runnable() { @Override @@ -432,8 +432,8 @@ public void subscribeSubscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final Flowable cache = Flowable.range(1, 500).cache(); - final TestSubscriberEx ts1 = new TestSubscriberEx(); - final TestSubscriberEx ts2 = new TestSubscriberEx(); + final TestSubscriberEx ts1 = new TestSubscriberEx<>(); + final TestSubscriberEx ts2 = new TestSubscriberEx<>(); Runnable r1 = new Runnable() { @Override @@ -476,7 +476,7 @@ public void subscribeCompleteRace() { cache.test(); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -511,4 +511,18 @@ public void backpressure() { .requestMore(3) .assertResult(1, 2, 3, 4, 5); } + + @Test + public void addRemoveRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Flowable f = Flowable.never().cache(); + + TestSubscriber ts = f.test(); + + TestHelper.race( + () -> ts.cancel(), + () -> f.test() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCastTest.java index c39b1972f0..dc26fdccaa 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java index 836fd8a4c3..0f07148179 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,8 +30,9 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.operators.flowable.FlowableZipTest.ArgsToString; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.*; @@ -437,8 +438,8 @@ public List apply(Object[] args) { }; for (int i = 1; i <= n; i++) { System.out.println("test1ToNSources: " + i + " sources"); - List> sources = new ArrayList>(); - List values = new ArrayList(); + List> sources = new ArrayList<>(); + List values = new ArrayList<>(); for (int j = 0; j < i; j++) { sources.add(Flowable.just(j)); values.add(j); @@ -468,8 +469,8 @@ public List apply(Object[] args) { }; for (int i = 1; i <= n; i++) { System.out.println("test1ToNSourcesScheduled: " + i + " sources"); - List> sources = new ArrayList>(); - List values = new ArrayList(); + List> sources = new ArrayList<>(); + List values = new ArrayList<>(); for (int j = 0; j < i; j++) { sources.add(Flowable.just(j).subscribeOn(Schedulers.io())); values.add(j); @@ -749,7 +750,7 @@ public void backpressure() { BiFunction combineLatestFunction = getConcatStringIntegerCombineLatestFunction(); int num = Flowable.bufferSize() * 4; - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.combineLatest( Flowable.just("one", "two"), Flowable.range(2, num), @@ -784,7 +785,7 @@ public void accept(Notification n) { } }).take(SIZE); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.combineLatest(timer, Flowable. never(), new BiFunction() { @Override @@ -802,7 +803,6 @@ public Long apply(Long t1, Integer t2) { @Test public void combineLatestRequestOverflow() throws InterruptedException { - @SuppressWarnings("unchecked") List> sources = Arrays.asList(Flowable.fromArray(1, 2, 3, 4), Flowable.fromArray(5, 6, 7, 8)); Flowable f = Flowable.combineLatest(sources, new Function() { @@ -864,7 +864,6 @@ public void accept(Throwable t) { assertFalse(errorOccurred.get()); } - @SuppressWarnings("unchecked") @Test public void combineLatestIterable() { Flowable source = Flowable.just(1); @@ -889,7 +888,7 @@ public Integer apply(Object[] args) { public void combineMany() { int n = Flowable.bufferSize() * 3; - List> sources = new ArrayList>(); + List> sources = new ArrayList<>(); StringBuilder expected = new StringBuilder(n * 2); @@ -916,7 +915,6 @@ public String apply(Object[] args) { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void firstJustError() { TestSubscriber ts = TestSubscriber.create(); @@ -936,7 +934,6 @@ public Integer apply(Object[] args) { ts.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void secondJustError() { TestSubscriber ts = TestSubscriber.create(); @@ -956,7 +953,6 @@ public Integer apply(Object[] args) { ts.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void oneErrors() { TestSubscriber ts = TestSubscriber.create(); @@ -976,7 +972,6 @@ public Integer apply(Object[] args) { ts.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void twoErrors() { TestSubscriber ts = TestSubscriber.create(); @@ -996,7 +991,6 @@ public Integer apply(Object[] args) { ts.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void bothError() { TestSubscriber ts = TestSubscriber.create(); @@ -1053,7 +1047,7 @@ public void combineLatestNArguments() throws Exception { if (j < i) { assertEquals("source" + (j + 1) + " is null", ex.getCause().getMessage()); } else { - assertEquals("f is null", ex.getCause().getMessage()); + assertEquals("combiner is null", ex.getCause().getMessage()); } } } @@ -1066,7 +1060,7 @@ public void combineLatestArrayNSources() { for (int i = 1; i < 100; i++) { Flowable[] sources = new Flowable[i]; Arrays.fill(sources, Flowable.just(1)); - List expected = new ArrayList(i); + List expected = new ArrayList<>(i); for (int j = 1; j <= i; j++) { expected.add(1); } @@ -1080,7 +1074,7 @@ public List apply(Object[] t) throws Exception { .test() .assertResult(expected); - Flowable.combineLatestDelayError(sources, new Function>() { + Flowable.combineLatestArrayDelayError(sources, new Function>() { @Override public List apply(Object[] t) throws Exception { return Arrays.asList(t); @@ -1111,7 +1105,7 @@ public Object apply(Object[] a) throws Exception { @SuppressWarnings("unchecked") public void combineLatestDelayErrorArrayOfSources() { - Flowable.combineLatestDelayError(new Flowable[] { + Flowable.combineLatestArrayDelayError(new Flowable[] { Flowable.just(1), Flowable.just(2) }, new Function() { @Override @@ -1127,7 +1121,7 @@ public Object apply(Object[] a) throws Exception { @SuppressWarnings("unchecked") public void combineLatestDelayErrorArrayOfSourcesWithError() { - Flowable.combineLatestDelayError(new Flowable[] { + Flowable.combineLatestArrayDelayError(new Flowable[] { Flowable.just(1), Flowable.just(2).concatWith(Flowable.error(new TestException())) }, new Function() { @Override @@ -1140,7 +1134,6 @@ public Object apply(Object[] a) throws Exception { } @Test - @SuppressWarnings("unchecked") public void combineLatestDelayErrorIterableOfSources() { Flowable.combineLatestDelayError(Arrays.asList( @@ -1156,7 +1149,6 @@ public Object apply(Object[] a) throws Exception { } @Test - @SuppressWarnings("unchecked") public void combineLatestDelayErrorIterableOfSourcesWithError() { Flowable.combineLatestDelayError(Arrays.asList( @@ -1180,7 +1172,7 @@ public void combineLatestArrayEmpty() { @SuppressWarnings("unchecked") @Test public void combineLatestDelayErrorEmpty() { - assertSame(Flowable.empty(), Flowable.combineLatestDelayError(new Flowable[0], Functions.identity(), 16)); + assertSame(Flowable.empty(), Flowable.combineLatestArrayDelayError(new Flowable[0], Functions.identity(), 16)); } @Test @@ -1207,7 +1199,7 @@ public Object apply(Object a, Object b) throws Exception { @Test public void cancelWhileSubscribing() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.combineLatest( Flowable.just(1) @@ -1304,7 +1296,7 @@ public Object apply(Object a, Object b) throws Exception { @SuppressWarnings("unchecked") @Test public void errorDelayed() { - Flowable.combineLatestDelayError( + Flowable.combineLatestArrayDelayError( new Publisher[] { Flowable.error(new TestException()), Flowable.just(1) }, new Function() { @Override @@ -1321,7 +1313,7 @@ public Object apply(Object[] a) throws Exception { @SuppressWarnings("unchecked") @Test public void errorDelayed2() { - Flowable.combineLatestDelayError( + Flowable.combineLatestArrayDelayError( new Publisher[] { Flowable.error(new TestException()).startWithItem(1), Flowable.empty() }, new Function() { @Override @@ -1366,7 +1358,6 @@ public Object apply(Object a, Object b) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void dontSubscribeIfDone2() { List errors = TestHelper.trackPluginErrors(); @@ -1400,12 +1391,11 @@ public Object apply(Object[] a) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void combine2Flowable2Errors() throws Exception { List errors = TestHelper.trackPluginErrors(); try { - TestSubscriber testObserver = TestSubscriber.create(); + TestSubscriber testSubscriber = TestSubscriber.create(); TestScheduler testScheduler = new TestScheduler(); @@ -1470,11 +1460,11 @@ public void run() throws Exception { System.out.println("combineLatestDelayError: doFinally"); } }) - .subscribe(testObserver); + .subscribe(testSubscriber); testScheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); - testObserver.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(5, TimeUnit.SECONDS); assertTrue(errors.toString(), errors.isEmpty()); } finally { @@ -1534,7 +1524,6 @@ public Integer apply(Integer t1, Integer t2) throws Exception { } @Test - @SuppressWarnings("unchecked") public void syncFirstErrorsAfterItemDelayError() { Flowable.combineLatestDelayError(Arrays.asList( Flowable.just(21).concatWith(Flowable.error(new TestException())), @@ -1552,7 +1541,6 @@ public Object apply(Object[] a) throws Exception { .assertFailure(TestException.class, 42); } - @SuppressWarnings("unchecked") @Test public void publishersInIterable() { Publisher source = new Publisher() { @@ -1571,4 +1559,230 @@ public Integer apply(Object[] t) throws Throwable { .test() .assertResult(2); } + + @Test + public void FlowableSourcesInIterable() { + Flowable source = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + Flowable.just(1).subscribe(s); + } + }; + + Flowable.combineLatest(Arrays.asList(source, source), new Function() { + @Override + public Integer apply(Object[] t) throws Throwable { + return 2; + } + }) + .test() + .assertResult(2); + } + + @Test + public void onCompleteDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestSubscriber ts = new TestSubscriber<>(); + PublishProcessor pp = PublishProcessor.create(); + + Flowable.combineLatest(pp, Flowable.never(), (a, b) -> a) + .subscribe(ts); + + TestHelper.race(() -> pp.onComplete(), () -> ts.cancel()); + } + } + + @Test + public void onErrorDisposeDelayErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex = new TestException(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestSubscriberEx ts = new TestSubscriberEx<>(); + AtomicReference> ref = new AtomicReference<>(); + Flowable f = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + ref.set(s); + } + }; + + Flowable.combineLatestDelayError(Arrays.asList(f, Flowable.never()), (a) -> a) + .subscribe(ts); + + ref.get().onSubscribe(new BooleanSubscription()); + + TestHelper.race(() -> ref.get().onError(ex), () -> ts.cancel()); + + if (ts.errors().isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + } + }); + } + + @Test + public void doneButNotEmpty() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = Flowable.combineLatest(pp1, pp2, (a, b) -> a + b) + .doOnNext(v -> { + if (v == 2) { + pp2.onNext(3); + pp2.onComplete(); + pp1.onComplete(); + } + }) + .test(); + + pp1.onNext(1); + pp2.onNext(1); + + ts.assertResult(2, 4); + } + + @Test + public void iterableNullPublisher() { + Flowable.combineLatest(Arrays.asList(Flowable.never(), null), (a) -> a) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.combineLatest(Flowable.never(), Flowable.never(), (a, b) -> a)); + } + + @Test + public void syncFusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Flowable.combineLatest(Flowable.never(), Flowable.never(), (a, b) -> a) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } + + @Test + public void bounderyFusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + + Flowable.combineLatest(Flowable.never(), Flowable.never(), (a, b) -> a) + .subscribe(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } + + @Test + public void fusedNormal() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(2), (a, b) -> a + b) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(3); + } + + @Test + public void fusedToParallel() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(2), (a, b) -> a + b) + .parallel() + .sequential() + .subscribeWith(ts) + .assertResult(3); + } + + @Test + public void fusedToParallel2() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(2), (a, b) -> a + b) + .compose(TestHelper.flowableStripBoundary()) + .parallel() + .sequential() + .subscribeWith(ts) + .assertResult(3); + } + + @Test + public void fusedError() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.error(new TestException()), (a, b) -> a + b) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertFailure(TestException.class); + } + + @Test + public void nonFusedMoreWorkBeforeTermination() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = Flowable.combineLatest(pp, Flowable.just(1), (a, b) -> a + b) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(0); + + ts.assertResult(1, 3); + } + + @Test + public void nonFusedDelayErrorMoreWorkBeforeTermination() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = Flowable.combineLatestDelayError(Arrays.asList(pp, Flowable.just(1)), a -> Arrays.asList(a)) + .doOnNext(v -> { + if (((Integer)v.get(0)) == 0) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(0); + + ts.assertResult(Arrays.asList(0, 1), Arrays.asList(2, 1)); + } + + @Test + public void fusedCombinerCrashError() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + Flowable.combineLatest(Flowable.just(1), Flowable.just(1), (a, b) -> { throw new TestException(); }) + .subscribeWith(ts) + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertFailure(TestException.class); + } + + @Test + public void fusedCombinerCrashError2() { + Flowable.combineLatest(Flowable.just(1), Flowable.just(1), (a, b) -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()) + .rebatchRequests(10) + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatDelayErrorTest.java index e5ce3766ea..39d6cf6ff5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatDelayErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatDelayErrorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -232,7 +232,7 @@ public void concatDelayErrorFlowable() { @Test public void concatDelayErrorFlowableError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.concatDelayError( withError(Flowable.just(withError(Flowable.just(1)), withError(Flowable.just(2))))) @@ -252,7 +252,6 @@ public void concatDelayErrorFlowableError() { assertTrue(cex.get(2).toString(), cex.get(2) instanceof TestException); } - @SuppressWarnings("unchecked") @Test public void concatDelayErrorIterable() { TestSubscriber ts = TestSubscriber.create(); @@ -266,10 +265,9 @@ public void concatDelayErrorIterable() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void concatDelayErrorIterableError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.concatDelayError( Arrays.asList(withError(Flowable.just(1)), withError(Flowable.just(2)))) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java index 5f41cce3dc..3711aecec4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapEagerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -276,8 +276,8 @@ public Flowable apply(Integer t) { @Before public void before() { - ts = new TestSubscriber(); - tsBp = new TestSubscriber(0L); + ts = new TestSubscriber<>(); + tsBp = new TestSubscriber<>(0L); } @Test @@ -298,7 +298,6 @@ public void simple2() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness2() { final AtomicInteger count = new AtomicInteger(); @@ -323,7 +322,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness3() { final AtomicInteger count = new AtomicInteger(); @@ -348,7 +346,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness4() { final AtomicInteger count = new AtomicInteger(); @@ -373,7 +370,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness5() { final AtomicInteger count = new AtomicInteger(); @@ -398,7 +394,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness6() { final AtomicInteger count = new AtomicInteger(); @@ -423,7 +418,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness7() { final AtomicInteger count = new AtomicInteger(); @@ -448,7 +442,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness8() { final AtomicInteger count = new AtomicInteger(); @@ -473,7 +466,6 @@ public void accept(Integer t) { tsBp.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness9() { final AtomicInteger count = new AtomicInteger(); @@ -507,7 +499,6 @@ public void mainError() { ts.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void innerError() { Flowable.concatArrayEager(Flowable.just(1), Flowable.error(new TestException())).subscribe(ts); @@ -517,7 +508,6 @@ public void innerError() { ts.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void innerEmpty() { Flowable.concatArrayEager(Flowable.empty(), Flowable.empty()).subscribe(ts); @@ -552,7 +542,6 @@ public void invalidCapacityHint() { } @Test - @SuppressWarnings("unchecked") public void backpressure() { Flowable.concatArrayEager(Flowable.just(1), Flowable.just(1)).subscribe(tsBp); @@ -642,7 +631,7 @@ public void accept(Integer t) { @Test public void maxConcurrent5() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.range(1, 100).doOnRequest(new LongConsumer() { @Override public void accept(long reqCount) { @@ -662,7 +651,6 @@ public void accept(long reqCount) { Assert.assertEquals(1, (long) requests.get(5)); } - @SuppressWarnings("unchecked") @Test public void capacityHint() { Flowable source = Flowable.just(1); @@ -699,7 +687,6 @@ public void flowableCapacityHint() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void badCapacityHint() throws Exception { Flowable source = Flowable.just(1); @@ -730,7 +717,6 @@ public void concatEagerZero() { .assertResult(); } - @SuppressWarnings("unchecked") @Test public void concatEagerOne() { Flowable.concatEager(Arrays.asList(Flowable.just(1))) @@ -738,7 +724,6 @@ public void concatEagerOne() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void concatEagerTwo() { Flowable.concatEager(Arrays.asList(Flowable.just(1), Flowable.just(2))) @@ -759,7 +744,7 @@ public void Flowable() { } @Test - public void ObservableCapacityHint() { + public void publisherCapacityHint() { Flowable source = Flowable.just(1); TestSubscriber ts = TestSubscriber.create(); @@ -770,7 +755,6 @@ public void ObservableCapacityHint() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void concatEagerIterable() { Flowable.concatEager(Arrays.asList(Flowable.just(1), Flowable.just(2))) @@ -898,14 +882,14 @@ public Integer call() throws Exception { @Test public void innerErrorAfterPoll() { - final UnicastProcessor us = UnicastProcessor.create(); - us.onNext(1); + final UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); TestSubscriber ts = new TestSubscriber() { @Override public void onNext(Integer t) { super.onNext(t); - us.onError(new TestException()); + up.onError(new TestException()); } }; @@ -913,7 +897,7 @@ public void onNext(Integer t) { .concatMapEager(new Function>() { @Override public Flowable apply(Integer v) throws Exception { - return us; + return up; } }, 1, 128) .subscribe(ts); @@ -955,7 +939,7 @@ public void run() { @Test public void mapperCancels() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).hide() .concatMapEager(new Function>() { @@ -989,12 +973,12 @@ public Integer apply(Integer v) throws Exception { @Test public void fuseAndTake() { - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - us.onNext(1); - us.onComplete(); + up.onNext(1); + up.onComplete(); - us.concatMapEager(new Function>() { + up.concatMapEager(new Function>() { @Override public Flowable apply(Integer v) throws Exception { return Flowable.just(1); @@ -1097,7 +1081,7 @@ public void drainCancelRaceOnEmpty() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishProcessor pp = PublishProcessor.create(); - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Flowable.just(1) .concatMapEager(Functions.justFunction(pp)) @@ -1157,7 +1141,7 @@ public Flowable apply(Integer i) throws Exception { public void maxConcurrencyOf2() { List[] list = new ArrayList[100]; for (int i = 0; i < 100; i++) { - List lst = new ArrayList(); + List lst = new ArrayList<>(); list[i] = lst; for (int k = 1; k <= 10; k++) { lst.add((i) * 10 + k); @@ -1193,7 +1177,6 @@ public void arrayDelayErrorDefault() { PublishProcessor pp2 = PublishProcessor.create(); PublishProcessor pp3 = PublishProcessor.create(); - @SuppressWarnings("unchecked") TestSubscriber ts = Flowable.concatArrayEagerDelayError(pp1, pp2, pp3) .test(); @@ -1227,7 +1210,6 @@ public void arrayDelayErrorMaxConcurrency() { PublishProcessor pp2 = PublishProcessor.create(); PublishProcessor pp3 = PublishProcessor.create(); - @SuppressWarnings("unchecked") TestSubscriber ts = Flowable.concatArrayEagerDelayError(2, 2, pp1, pp2, pp3) .test(); @@ -1263,7 +1245,6 @@ public void arrayDelayErrorMaxConcurrencyErrorDelayed() { PublishProcessor pp2 = PublishProcessor.create(); PublishProcessor pp3 = PublishProcessor.create(); - @SuppressWarnings("unchecked") TestSubscriber ts = Flowable.concatArrayEagerDelayError(2, 2, pp1, pp2, pp3) .test(); @@ -1370,4 +1351,84 @@ public Flowable apply(Integer v) throws Throwable { } }); } + + @Test + public void iterableDelayError() { + Flowable.concatEagerDelayError(Arrays.asList( + Flowable.range(1, 2), + Flowable.error(new TestException()), + Flowable.range(3, 3) + )) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void iterableDelayErrorMaxConcurrency() { + Flowable.concatEagerDelayError(Arrays.asList( + Flowable.range(1, 2), + Flowable.error(new TestException()), + Flowable.range(3, 3) + ), 1, 1) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void publisherDelayError() { + Flowable.concatEagerDelayError(Flowable.fromArray( + Flowable.range(1, 2), + Flowable.error(new TestException()), + Flowable.range(3, 3) + )) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void publisherDelayErrorMaxConcurrency() { + Flowable.concatEagerDelayError(Flowable.fromArray( + Flowable.range(1, 2), + Flowable.error(new TestException()), + Flowable.range(3, 3) + ), 1, 1) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void innerSyncFused() { + Flowable.just(1) + .hide() + .concatMapEagerDelayError(v -> Flowable.range(1, 10), true, 1, 1) + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().concatMapEagerDelayError(v -> Flowable.never(), false)); + } + + @Test + public void cancelAfterOnNext() { + Flowable.just(1) + .hide() + .concatMapEagerDelayError(v -> Flowable.range(1, 5).hide(), true) + .takeUntil(v -> true) + .test() + .assertResult(1); + } + + @Test + public void noInnerQueue() { + Flowable.just(1) + .hide() + .concatMapEagerDelayError(v -> Flowable.fromPublisher(s -> { }), true) + .test(0L) + .assertEmpty() + .requestMore(1L) + .assertEmpty() + ; + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java index fc8d665559..027157ce02 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,17 +18,18 @@ import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; -import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -65,6 +66,56 @@ public Publisher apply(String v) .assertResult("RxSingleScheduler"); } + @Test + public void innerScalarRequestRace() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMap(v -> v, n + 1, ImmediateThinScheduler.INSTANCE) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + + @Test + public void innerScalarRequestRaceDelayError() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMapDelayError(v -> v, true, n + 1, ImmediateThinScheduler.INSTANCE) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + @Test public void boundaryFusionDelayError() { Flowable.range(1, 10000) @@ -304,7 +355,7 @@ public Flowable apply(Integer t) throws Throwable { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -349,7 +400,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeoutMillis = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeoutMillis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeoutMillis + " milliseconds."); + } assertEquals(n, counter.get()); } @@ -366,7 +421,7 @@ public void concatMapRangeAsyncLoopIssue2876() { if (i % 1000 == 0) { System.out.println("concatMapRangeAsyncLoop > " + i); } - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(0, 1000) .concatMap(new Function>() { @Override @@ -569,7 +624,7 @@ protected void subscribeActual(Subscriber s) { } .concatMap(Functions.justFunction(Flowable.just(2)), 8, ImmediateThinScheduler.INSTANCE) .test(0L) - .assertFailure(IllegalStateException.class); + .assertFailure(QueueOverflowException.class); } @Test @@ -1088,4 +1143,141 @@ public Publisher apply(Integer v) throws Throwable { } }); } + + @Test + public void fusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + TestHelper.rejectFlowableFusion() + .concatMap(v -> Flowable.never(), 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + } + + @Test + public void fusionRejectedDelayErrorr() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + TestHelper.rejectFlowableFusion() + .concatMapDelayError(v -> Flowable.never(), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + } + + @Test + public void scalarInnerJustDispose() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .concatMap(v -> Flowable.fromCallable(() -> { + ts.cancel(); + return 1; + }), 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void scalarInnerJustDisposeDelayError() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .concatMapDelayError(v -> Flowable.fromCallable(() -> { + ts.cancel(); + return 1; + }), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + + ts.assertEmpty(); + } + + static final class EmptyDisposingFlowable extends Flowable + implements Supplier { + final TestSubscriber ts; + EmptyDisposingFlowable(TestSubscriber ts) { + this.ts = ts; + } + + @Override + protected void subscribeActual(@NonNull Subscriber subscriber) { + EmptySubscription.complete(subscriber); + } + + @Override + public @NonNull Object get() throws Throwable { + ts.cancel(); + return null; + } + } + + @Test + public void scalarInnerEmptyDisposeDelayError() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .concatMapDelayError(v -> new EmptyDisposingFlowable(ts), + true, 2, ImmediateThinScheduler.INSTANCE + ) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void mainErrorInnerNextIgnoreCancel() { + AtomicReference> ref = new AtomicReference<>(); + + Flowable.just(1).concatWith(Flowable.error(new TestException())) + .concatMap(v -> Flowable.fromPublisher(ref::set), 2, ImmediateThinScheduler.INSTANCE) + .doOnError(e -> { + ref.get().onSubscribe(new BooleanSubscription()); + ref.get().onNext(1); + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void scalarSupplierMainError() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.concatMap(v -> Flowable.fromCallable(() -> { + pp.onError(new TestException()); + return 2; + }), 2, ImmediateThinScheduler.INSTANCE) + .test() + ; + + pp.onNext(1); + + ts.assertFailure(TestException.class); + } + + @Test + public void mainErrorInnerErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex1 = new TestException(); + TestException ex2 = new TestException(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + TestSubscriber ts = Flowable.fromPublisher(ref1::set) + .concatMap(v -> Flowable.fromPublisher(ref2::set), 2, ImmediateThinScheduler.INSTANCE) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref2.get().onSubscribe(new BooleanSubscription()); + + TestHelper.race(() -> ref1.get().onError(ex1), () -> ref2.get().onError(ex2)); + + ts.assertError(RuntimeException.class); + errors.clear(); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java index 74394b122e..10df043ba5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap.WeakScalarSubscription; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableConcatMap.SimpleScalarSubscription; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -32,9 +33,9 @@ public class FlowableConcatMapTest extends RxJavaTest { @Test - public void weakSubscriptionRequest() { - TestSubscriber ts = new TestSubscriber(0); - WeakScalarSubscription ws = new WeakScalarSubscription(1, ts); + public void simpleSubscriptionRequest() { + TestSubscriber ts = new TestSubscriber<>(0); + SimpleScalarSubscription ws = new SimpleScalarSubscription<>(1, ts); ts.onSubscribe(ws); ws.request(0); @@ -78,6 +79,56 @@ public Publisher apply(String v) .assertResult("RxSingleScheduler"); } + @Test + public void innerScalarRequestRace() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMap(v -> v, n + 1) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + + @Test + public void innerScalarRequestRaceDelayError() { + Flowable just = Flowable.just(1); + int n = 1000; + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor> source = PublishProcessor.create(); + + TestSubscriber ts = source + .concatMapDelayError(v -> v, true, n + 1) + .test(1L); + + TestHelper.race(() -> { + for (int j = 0; j < n; j++) { + source.onNext(just); + } + }, () -> { + for (int j = 0; j < n; j++) { + ts.request(1); + } + }); + + ts.assertValueCount(n); + } + } + @Test public void boundaryFusionDelayError() { Flowable.range(1, 10000) @@ -252,4 +303,23 @@ public Publisher apply(Integer v) throws Throwable { } }); } + + @Test + public void asyncFusedSource() { + UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); + up.onComplete(); + + up.concatMap(v -> Flowable.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void scalarCallableSource() { + Flowable.fromCallable(() -> 1) + .concatMap(v -> Flowable.just(1)) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java index eddc091c77..5a0634ffb6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.io.IOException; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.*; @@ -27,7 +28,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -65,7 +66,7 @@ public void concatWithList() { final Flowable odds = Flowable.fromArray(o); final Flowable even = Flowable.fromArray(e); - final List> list = new ArrayList>(); + final List> list = new ArrayList<>(); list.add(odds); list.add(even); Flowable concat = Flowable.concat(Flowable.fromIterable(list)); @@ -110,8 +111,8 @@ public void subscribe(Subscriber> subscriber) { public void simpleAsyncConcat() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestObservable o1 = new TestObservable("one", "two", "three"); - TestObservable o2 = new TestObservable("four", "five", "six"); + TestObservable o1 = new TestObservable<>("one", "two", "three"); + TestObservable o2 = new TestObservable<>("four", "five", "six"); Flowable.concat(Flowable.unsafeCreate(o1), Flowable.unsafeCreate(o2)).subscribe(subscriber); @@ -150,12 +151,12 @@ public void nestedAsyncConcatLoop() throws Throwable { public void nestedAsyncConcat() throws InterruptedException { Subscriber subscriber = TestHelper.mockSubscriber(); - final TestObservable o1 = new TestObservable("one", "two", "three"); - final TestObservable o2 = new TestObservable("four", "five", "six"); - final TestObservable o3 = new TestObservable("seven", "eight", "nine"); + final TestObservable o1 = new TestObservable<>("one", "two", "three"); + final TestObservable o2 = new TestObservable<>("four", "five", "six"); + final TestObservable o3 = new TestObservable<>("seven", "eight", "nine"); final CountDownLatch allowThird = new CountDownLatch(1); - final AtomicReference parent = new AtomicReference(); + final AtomicReference parent = new AtomicReference<>(); final CountDownLatch parentHasStarted = new CountDownLatch(1); final CountDownLatch parentHasFinished = new CountDownLatch(1); @@ -163,7 +164,7 @@ public void nestedAsyncConcat() throws InterruptedException { @Override public void subscribe(final Subscriber> subscriber) { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); subscriber.onSubscribe(new Subscription() { @Override public void request(long n) { @@ -283,8 +284,8 @@ public void blockedObservableOfObservables() { final Flowable even = Flowable.fromArray(e); final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even); + + TestObservable> observableOfObservables = new TestObservable<>(callOnce, okToContinue, odds, even); Flowable concatF = Flowable.concat(Flowable.unsafeCreate(observableOfObservables)); concatF.subscribe(subscriber); try { @@ -316,14 +317,13 @@ public void blockedObservableOfObservables() { @Test public void concatConcurrentWithInfinity() { - final TestObservable w1 = new TestObservable("one", "two", "three"); + final TestObservable w1 = new TestObservable<>("one", "two", "three"); //This observable will send "hello" MAX_VALUE time. - final TestObservable w2 = new TestObservable("hello", Integer.MAX_VALUE); + final TestObservable w2 = new TestObservable<>("hello", Integer.MAX_VALUE); Subscriber subscriber = TestHelper.mockSubscriber(); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Flowable.unsafeCreate(w1), Flowable.unsafeCreate(w2)); + TestObservable> observableOfObservables = new TestObservable<>(Flowable.unsafeCreate(w1), Flowable.unsafeCreate(w2)); Flowable concatF = Flowable.concat(Flowable.unsafeCreate(observableOfObservables)); concatF.take(50).subscribe(subscriber); @@ -351,8 +351,8 @@ public void concatNonBlockingObservables() { final CountDownLatch okToContinueW1 = new CountDownLatch(1); final CountDownLatch okToContinueW2 = new CountDownLatch(1); - final TestObservable w1 = new TestObservable(null, okToContinueW1, "one", "two", "three"); - final TestObservable w2 = new TestObservable(null, okToContinueW2, "four", "five", "six"); + final TestObservable w1 = new TestObservable<>(null, okToContinueW1, "one", "two", "three"); + final TestObservable w2 = new TestObservable<>(null, okToContinueW2, "four", "five", "six"); Subscriber subscriber = TestHelper.mockSubscriber(); @@ -402,11 +402,11 @@ public void subscribe(Subscriber> subscriber) { public void concatUnsubscribe() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); - final TestObservable w1 = new TestObservable("one", "two", "three"); - final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); + final TestObservable w1 = new TestObservable<>("one", "two", "three"); + final TestObservable w2 = new TestObservable<>(callOnce, okToContinue, "four", "five", "six"); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber, 0L); + TestSubscriber ts = new TestSubscriber<>(subscriber, 0L); final Flowable concat = Flowable.concat(Flowable.unsafeCreate(w1), Flowable.unsafeCreate(w2)); @@ -444,14 +444,13 @@ public void concatUnsubscribe() { public void concatUnsubscribeConcurrent() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); - final TestObservable w1 = new TestObservable("one", "two", "three"); - final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); + final TestObservable w1 = new TestObservable<>("one", "two", "three"); + final TestObservable w2 = new TestObservable<>(callOnce, okToContinue, "four", "five", "six"); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber, 0L); + TestSubscriber ts = new TestSubscriber<>(subscriber, 0L); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Flowable.unsafeCreate(w1), Flowable.unsafeCreate(w2)); + TestObservable> observableOfObservables = new TestObservable<>(Flowable.unsafeCreate(w1), Flowable.unsafeCreate(w2)); Flowable concatF = Flowable.concat(Flowable.unsafeCreate(observableOfObservables)); concatF.subscribe(ts); @@ -506,10 +505,12 @@ public void cancel() { private final T seed; private final int size; + @SafeVarargs TestObservable(T... values) { this(null, null, values); } + @SafeVarargs TestObservable(CountDownLatch once, CountDownLatch okToContinue, T... values) { this.values = Arrays.asList(values); this.size = this.values.size(); @@ -630,7 +631,7 @@ public Flowable apply(Integer v) { result.subscribe(o); - List list = new ArrayList(n); + List list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(i); } @@ -655,7 +656,7 @@ public Flowable apply(Integer v) { result.subscribe(o); - List list = new ArrayList(n); + List list = new ArrayList<>(n); for (int i = 0; i < n / 2; i++) { list.add(i); } @@ -674,7 +675,7 @@ public void concatOuterBackpressure() { @Test public void innerBackpressureWithAlignedBoundaries() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, Flowable.bufferSize() * 2) .concatWith(Flowable.range(0, Flowable.bufferSize() * 2)) .observeOn(Schedulers.computation()) // observeOn has a backpressured RxRingBuffer @@ -693,7 +694,7 @@ public void innerBackpressureWithAlignedBoundaries() { */ @Test public void innerBackpressureWithoutAlignedBoundaries() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, (Flowable.bufferSize() * 2) + 10) .concatWith(Flowable.range(0, (Flowable.bufferSize() * 2) + 10)) .observeOn(Schedulers.computation()) // observeOn has a backpressured RxRingBuffer @@ -719,7 +720,7 @@ public void subscribe(Subscriber s) { }); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.concat(f, f).subscribe(ts); ts.awaitDone(500, TimeUnit.MILLISECONDS); ts.assertTerminated(); @@ -728,7 +729,7 @@ public void subscribe(Subscriber s) { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -773,7 +774,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeoutMillis = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeoutMillis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeoutMillis + " milliseconds."); + } assertEquals(n, counter.get()); } @@ -815,7 +820,7 @@ public void concatMapRangeAsyncLoopIssue2876() { if (i % 1000 == 0) { System.out.println("concatMapRangeAsyncLoop > " + i); } - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(0, 1000) .concatMap(new Function>() { @Override @@ -1055,7 +1060,6 @@ public void concat4() { .assertResult(1, 1, 1, 1); } - @SuppressWarnings("unchecked") @Test public void concatArrayDelayError() { Flowable.concatArrayDelayError(Flowable.just(1), Flowable.just(2), @@ -1064,7 +1068,6 @@ public void concatArrayDelayError() { .assertResult(1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatArrayDelayErrorWithError() { Flowable.concatArrayDelayError(Flowable.just(1), Flowable.just(2), @@ -1074,7 +1077,6 @@ public void concatArrayDelayErrorWithError() { .assertFailure(TestException.class, 1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatIterableDelayError() { Flowable.concatDelayError( @@ -1084,7 +1086,6 @@ public void concatIterableDelayError() { .assertResult(1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatIterableDelayErrorWithError() { Flowable.concatDelayError( @@ -1163,13 +1164,11 @@ public Iterable apply(Integer v) throws Exception { .assertResult(1, 2, 3, 4, 5, 1, 2, 3, 4, 5); } - @SuppressWarnings("unchecked") @Test public void emptyArray() { assertSame(Flowable.empty(), Flowable.concatArrayDelayError()); } - @SuppressWarnings("unchecked") @Test public void singleElementArray() { assertSame(Flowable.never(), Flowable.concatArrayDelayError(Flowable.never())); @@ -1200,13 +1199,11 @@ public Flowable apply(Object v) throws Exception { } - @SuppressWarnings("unchecked") @Test public void concatArrayEmpty() { assertSame(Flowable.empty(), Flowable.concatArray()); } - @SuppressWarnings("unchecked") @Test public void concatArraySingleElement() { assertSame(Flowable.never(), Flowable.concatArray(Flowable.never())); @@ -1294,7 +1291,7 @@ protected void subscribeActual(Subscriber s) { } .concatMap(Functions.justFunction(Flowable.just(2)), 8) .test(0L) - .assertFailure(IllegalStateException.class); + .assertFailure(QueueOverflowException.class); } @Test @@ -1537,7 +1534,6 @@ public Publisher apply(Integer v) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscription() { final int[] calls = { 0 }; @@ -1558,7 +1554,6 @@ public void subscribe(FlowableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionDelayError() { final int[] calls = { 0 }; @@ -1579,7 +1574,6 @@ public void subscribe(FlowableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionIterable() { final int[] calls = { 0 }; @@ -1600,7 +1594,6 @@ public void subscribe(FlowableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionDelayErrorIterable() { final int[] calls = { 0 }; @@ -1621,7 +1614,6 @@ public void subscribe(FlowableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noCancelPreviousArray() { final AtomicInteger counter = new AtomicInteger(); @@ -1640,7 +1632,6 @@ public void run() throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noCancelPreviousIterable() { final AtomicInteger counter = new AtomicInteger(); @@ -1658,4 +1649,28 @@ public void run() throws Exception { assertEquals(0, counter.get()); } + + @Test + public void arrayDelayErrorMultipleErrors() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.concatArrayDelayError(Flowable.error(new IOException()), Flowable.error(new TestException())) + .subscribe(ts); + + ts.assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, IOException.class, TestException.class); + } + + @Test + public void arrayDelayErrorMultipleNullErrors() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.concatArrayDelayError(null, null) + .subscribe(ts); + + ts.assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, NullPointerException.class, NullPointerException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletableTest.java index a89897090a..845c814dbc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public class FlowableConcatWithCompletableTest extends RxJavaTest { @Test public void normal() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Completable.fromAction(new Action() { @@ -49,7 +49,7 @@ public void run() throws Exception { @Test public void mainError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.error(new TestException()) .concatWith(Completable.fromAction(new Action() { @@ -65,7 +65,7 @@ public void run() throws Exception { @Test public void otherError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Completable.error(new TestException())) @@ -76,7 +76,7 @@ public void otherError() { @Test public void takeMain() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Completable.fromAction(new Action() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybeTest.java index ea9e8f2330..5b45eafc7f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class FlowableConcatWithMaybeTest extends RxJavaTest { @Test public void normalEmpty() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Maybe.fromAction(new Action() { @@ -43,7 +43,7 @@ public void run() throws Exception { @Test public void normalNonEmpty() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Maybe.just(100)) @@ -68,7 +68,7 @@ public void backpressure() { @Test public void mainError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.error(new TestException()) .concatWith(Maybe.fromAction(new Action() { @@ -84,7 +84,7 @@ public void run() throws Exception { @Test public void otherError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Maybe.error(new TestException())) @@ -95,7 +95,7 @@ public void otherError() { @Test public void takeMain() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Maybe.fromAction(new Action() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingleTest.java index c846a2f5d5..8a7ed3cd65 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableConcatWithSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public class FlowableConcatWithSingleTest extends RxJavaTest { @Test public void normal() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Single.just(100)) @@ -51,7 +51,7 @@ public void backpressure() { @Test public void mainError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.error(new TestException()) .concatWith(Single.just(100)) @@ -62,7 +62,7 @@ public void mainError() { @Test public void otherError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Single.error(new TestException())) @@ -73,7 +73,7 @@ public void otherError() { @Test public void takeMain() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5) .concatWith(Single.just(100)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountTest.java index 158a3232f0..608138e357 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCountTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java index 023f5b5a25..4c3b735476 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableCreateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,38 +17,27 @@ import java.io.IOException; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Cancellable; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; public class FlowableCreateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void sourceNull() { - Flowable.create(null, BackpressureStrategy.BUFFER); - } - - @Test(expected = NullPointerException.class) - public void modeNull() { - Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(FlowableEmitter s) throws Exception { } - }, null); - } - @Test public void basic() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Flowable.create(new FlowableOnSubscribe() { @Override @@ -81,8 +70,8 @@ public void subscribe(FlowableEmitter e) throws Exception { public void basicWithCancellable() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d1 = Disposables.empty(); - final Disposable d2 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); + final Disposable d2 = Disposable.empty(); Flowable.create(new FlowableOnSubscribe() { @Override @@ -122,7 +111,7 @@ public void cancel() throws Exception { public void basicWithError() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Flowable.create(new FlowableOnSubscribe() { @Override @@ -153,7 +142,7 @@ public void subscribe(FlowableEmitter e) throws Exception { public void basicSerialized() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Flowable.create(new FlowableOnSubscribe() { @Override @@ -188,7 +177,7 @@ public void subscribe(FlowableEmitter e) throws Exception { public void basicWithErrorSerialized() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Flowable.create(new FlowableOnSubscribe() { @Override @@ -767,18 +756,13 @@ public void subscribe(FlowableEmitter e) throws Exception { } } - @Test(expected = NullPointerException.class) - public void nullArgument() { - Flowable.create(null, BackpressureStrategy.MISSING); - } - @Test public void onErrorCrash() { for (BackpressureStrategy m : BackpressureStrategy.values()) { Flowable.create(new FlowableOnSubscribe() { @Override public void subscribe(FlowableEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { e.onError(new IOException()); @@ -816,7 +800,7 @@ public void onCompleteCrash() { Flowable.create(new FlowableOnSubscribe() { @Override public void subscribe(FlowableEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { e.onComplete(); @@ -1063,7 +1047,7 @@ public void subscribe(FlowableEmitter e) throws Exception { @Test public void emittersHasToString() { Map> emitterMap = - new HashMap>(); + new HashMap<>(); emitterMap.put(BackpressureStrategy.MISSING, FlowableCreate.MissingEmitter.class); emitterMap.put(BackpressureStrategy.ERROR, FlowableCreate.ErrorAsyncEmitter.class); @@ -1081,4 +1065,66 @@ public void subscribe(FlowableEmitter emitter) throws Exception { }, entry.getKey()).test().assertEmpty(); } } + + @Test + public void serializedMissingMoreWorkWithComplete() { + AtomicReference> ref = new AtomicReference<>(); + + Flowable.create(emitter -> { + emitter = emitter.serialize(); + ref.set(emitter); + assertEquals(Long.MAX_VALUE, emitter.requested()); + emitter.onNext(1); + }, BackpressureStrategy.MISSING) + .doOnNext(v -> { + if (v == 1) { + ref.get().onNext(2); + ref.get().onComplete(); + } + }) + .test() + .assertResult(1, 2); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.create(e -> { }, BackpressureStrategy.BUFFER)); + } + + @Test + public void tryOnErrorNull() { + Flowable.create(emitter -> emitter.tryOnError(null), BackpressureStrategy.MISSING) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void serializedCompleteOnNext() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.create(emitter -> { + emitter = emitter.serialize(); + + emitter.onComplete(); + emitter.onNext(1); + }, BackpressureStrategy.MISSING) + .subscribe(ts); + + ts.assertResult(); + } + + @Test + public void serializedCancelOnNext() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.create(emitter -> { + emitter = emitter.serialize(); + + ts.cancel(); + emitter.onNext(1); + }, BackpressureStrategy.MISSING) + .subscribe(ts); + + ts.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTest.java index 17ae8ef654..01122106c8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDebounceTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.functions.Action; import org.junit.*; import org.mockito.InOrder; import org.reactivestreams.*; @@ -41,16 +42,86 @@ public class FlowableDebounceTest extends RxJavaTest { private TestScheduler scheduler; - private Subscriber Subscriber; + private Subscriber subscriber; private Scheduler.Worker innerScheduler; @Before public void before() { scheduler = new TestScheduler(); - Subscriber = TestHelper.mockSubscriber(); + subscriber = TestHelper.mockSubscriber(); innerScheduler = scheduler.createWorker(); } + @Test + public void debounceWithOnDroppedCallbackWithEx() throws Throwable { + Flowable source = Flowable.unsafeCreate(new Publisher() { + @Override + public void subscribe(Subscriber subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + publishNext(subscriber, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. + publishNext(subscriber, 400, "two"); // Should be published since "three" will arrive after the timeout expires. + publishNext(subscriber, 900, "three"); // Should be skipped since "four" will arrive before the timout expires. + publishNext(subscriber, 999, "four"); // Should be skipped since onComplete will arrive before the timeout expires. + publishCompleted(subscriber, 1000); // Should be published as soon as the timeout expires. + } + }); + + Action whenDisposed = mock(Action.class); + + Flowable sampled = source + .doOnCancel(whenDisposed) + .debounce(400, TimeUnit.MILLISECONDS, scheduler, e -> { + if ("three".equals(e)) { + throw new TestException("forced"); + } + }); + sampled.subscribe(subscriber); + + InOrder inOrder = inOrder(subscriber); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(subscriber, times(1)).onNext("two"); + inOrder.verify(subscriber, times(1)).onError(any(TestException.class)); + inOrder.verify(subscriber, times(0)).onNext("three"); + inOrder.verify(subscriber, times(0)).onNext("four"); + inOrder.verify(subscriber, times(0)).onComplete(); + inOrder.verifyNoMoreInteractions(); + verify(whenDisposed).run(); + } + + @Test + public void debounceWithOnDroppedCallback() { + Flowable source = Flowable.unsafeCreate(new Publisher() { + @Override + public void subscribe(Subscriber subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + publishNext(subscriber, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. + publishNext(subscriber, 400, "two"); // Should be published since "three" will arrive after the timeout expires. + publishNext(subscriber, 900, "three"); // Should be skipped since "four" will arrive before the timout expires. + publishNext(subscriber, 999, "four"); // Should be skipped since onComplete will arrive before the timeout expires. + publishCompleted(subscriber, 1000); // Should be published as soon as the timeout expires. + } + }); + + Observer dropCallbackObserver = TestHelper.mockObserver(); + Flowable sampled = source.debounce(400, TimeUnit.MILLISECONDS, scheduler, dropCallbackObserver::onNext); + sampled.subscribe(subscriber); + + scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(subscriber); + InOrder dropCallbackOrder = inOrder(dropCallbackObserver); + + // must go to 800 since it must be 400 after when two is sent, which is at 400 + scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS); + inOrder.verify(subscriber, times(1)).onNext("two"); + dropCallbackOrder.verify(dropCallbackObserver, times(1)).onNext("one"); + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + dropCallbackOrder.verify(dropCallbackObserver, times(1)).onNext("three"); + inOrder.verify(subscriber, times(1)).onComplete(); + inOrder.verifyNoMoreInteractions(); + dropCallbackOrder.verifyNoMoreInteractions(); + } + @Test public void debounceWithCompleted() { Flowable source = Flowable.unsafeCreate(new Publisher() { @@ -65,15 +136,15 @@ public void subscribe(Subscriber subscriber) { }); Flowable sampled = source.debounce(400, TimeUnit.MILLISECONDS, scheduler); - sampled.subscribe(Subscriber); + sampled.subscribe(subscriber); scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(Subscriber); + InOrder inOrder = inOrder(subscriber); // must go to 800 since it must be 400 after when two is sent, which is at 400 scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS); - inOrder.verify(Subscriber, times(1)).onNext("two"); + inOrder.verify(subscriber, times(1)).onNext("two"); scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); - inOrder.verify(Subscriber, times(1)).onComplete(); + inOrder.verify(subscriber, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @@ -97,13 +168,13 @@ public void subscribe(Subscriber subscriber) { }); Flowable sampled = source.debounce(200, TimeUnit.MILLISECONDS, scheduler); - sampled.subscribe(Subscriber); + sampled.subscribe(subscriber); scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(Subscriber); - inOrder.verify(Subscriber, times(0)).onNext(anyString()); + InOrder inOrder = inOrder(subscriber); + inOrder.verify(subscriber, times(0)).onNext(anyString()); scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); - inOrder.verify(Subscriber, times(1)).onComplete(); + inOrder.verify(subscriber, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @@ -121,15 +192,15 @@ public void subscribe(Subscriber subscriber) { }); Flowable sampled = source.debounce(400, TimeUnit.MILLISECONDS, scheduler); - sampled.subscribe(Subscriber); + sampled.subscribe(subscriber); scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); - InOrder inOrder = inOrder(Subscriber); + InOrder inOrder = inOrder(subscriber); // 100 + 400 means it triggers at 500 scheduler.advanceTimeTo(500, TimeUnit.MILLISECONDS); - inOrder.verify(Subscriber).onNext("one"); + inOrder.verify(subscriber).onNext("one"); scheduler.advanceTimeTo(701, TimeUnit.MILLISECONDS); - inOrder.verify(Subscriber).onError(any(TestException.class)); + inOrder.verify(subscriber).onError(any(TestException.class)); inOrder.verifyNoMoreInteractions(); } @@ -289,7 +360,7 @@ public Flowable apply(Integer t1) { @Test public void debounceWithTimeBackpressure() throws InterruptedException { TestScheduler scheduler = new TestScheduler(); - TestSubscriberEx subscriber = new TestSubscriberEx(); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); Flowable.merge( Flowable.just(1), Flowable.just(2).delay(10, TimeUnit.MILLISECONDS, scheduler) @@ -305,7 +376,7 @@ public void debounceWithTimeBackpressure() throws InterruptedException { @Test public void debounceDefaultScheduler() throws Exception { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 1000).debounce(1, TimeUnit.SECONDS).subscribe(ts); @@ -330,7 +401,7 @@ public void dispose() { TestHelper.checkDisposed(PublishProcessor.create().debounce(Functions.justFunction(Flowable.never()))); - Disposable d = new FlowableDebounceTimed.DebounceEmitter(1, 1, null); + Disposable d = new FlowableDebounceTimed.DebounceEmitter<>(1, 1, null); assertFalse(d.isDisposed()); d.dispose(); @@ -426,7 +497,7 @@ public Flowable apply(Flowable f) throws Exception { @Test public void disposeInOnNext() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); BehaviorProcessor.createDefault(1) .debounce(new Function>() { @@ -444,7 +515,7 @@ public Flowable apply(Integer o) throws Exception { @Test public void disposedInOnComplete() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); new Flowable() { @Override @@ -461,7 +532,7 @@ protected void subscribeActual(Subscriber subscriber) { @Test public void emitLate() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); TestSubscriber ts = Flowable.range(1, 2) .debounce(new Function>() { @@ -505,7 +576,7 @@ public Publisher apply(Flowable f) @Test public void timedDisposedIgnoredBySource() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); new Flowable() { @Override @@ -528,13 +599,13 @@ public void timedBadRequest() { @Test public void timedLateEmit() { - TestSubscriber ts = new TestSubscriber(); - DebounceTimedSubscriber sub = new DebounceTimedSubscriber( - ts, 1, TimeUnit.SECONDS, new TestScheduler().createWorker()); + TestSubscriber ts = new TestSubscriber<>(); + DebounceTimedSubscriber sub = new DebounceTimedSubscriber<>( + ts, 1, TimeUnit.SECONDS, new TestScheduler().createWorker(), null); sub.onSubscribe(new BooleanSubscription()); - DebounceEmitter de = new DebounceEmitter(1, 50, sub); + DebounceEmitter de = new DebounceEmitter<>(1, 50, sub); de.emit(); de.emit(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefaultIfEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefaultIfEmptyTest.java index ff99ca80e1..fc2c7e15a8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefaultIfEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDefaultIfEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -57,7 +57,7 @@ public void defaultIfEmptyWithEmpty() { @Test public void backpressureEmpty() { - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); Flowable.empty().defaultIfEmpty(1).subscribe(ts); ts.assertNoValues(); ts.assertNotTerminated(); @@ -69,7 +69,7 @@ public void backpressureEmpty() { @Test public void backpressureNonEmpty() { - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); Flowable.just(1, 2, 3).defaultIfEmpty(1).subscribe(ts); ts.assertNoValues(); ts.assertNotTerminated(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDeferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDeferTest.java index 8a1b48d4e9..caec2443fc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDeferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDeferTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java index 9ac97e12ab..57a8af4586 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelaySubscriptionOtherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import java.util.concurrent.*; @@ -31,7 +32,7 @@ public class FlowableDelaySubscriptionOtherTest extends RxJavaTest { public void noPrematureSubscription() { PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -64,7 +65,7 @@ public void accept(Subscription s) { public void noMultipleSubscriptions() { PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -98,7 +99,7 @@ public void accept(Subscription s) { public void completeTriggersSubscription() { PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -131,7 +132,7 @@ public void accept(Subscription s) { public void noPrematureSubscriptionToError() { PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -164,7 +165,7 @@ public void accept(Subscription s) { public void noSubscriptionIfOtherErrors() { PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -198,7 +199,7 @@ public void backpressurePassesThrough() { PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); final AtomicInteger subscribed = new AtomicInteger(); @@ -249,7 +250,7 @@ public void unsubscriptionPropagatesBeforeSubscribe() { PublishProcessor source = PublishProcessor.create(); PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.delaySubscription(other).subscribe(ts); @@ -267,7 +268,7 @@ public void unsubscriptionPropagatesAfterSubscribe() { PublishProcessor source = PublishProcessor.create(); PublishProcessor other = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.delaySubscription(other).subscribe(ts); @@ -308,11 +309,6 @@ public void accept(Subscription s) { Assert.assertFalse(subscribed.get()); } - @Test(expected = NullPointerException.class) - public void otherNull() { - Flowable.just(1).delaySubscription((Flowable)null); - } - @Test public void badSourceOther() { TestHelper.checkBadSourceFlowable(new Function, Object>() { @@ -348,4 +344,19 @@ public void subscribe(FlowableEmitter emitter) throws Exception { exec.shutdown(); } } + + @Test + public void doubleOnSubscribeMain() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.delaySubscription(Flowable.empty())); + } + + @Test + public void doubleOnSubscribeOther() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> PublishProcessor.create().delaySubscription(f)); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(PublishProcessor.create().delaySubscription(Flowable.empty())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelayTest.java index c5167f4fcd..4d67594a17 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDelayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,6 +20,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; +import java.util.concurrent.locks.LockSupport; import org.junit.*; import org.mockito.InOrder; @@ -28,6 +29,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.*; @@ -218,7 +220,7 @@ public void delaySubscriptionCancelBeforeTime() { Flowable result = Flowable.just(1, 2, 3).delaySubscription(100, TimeUnit.MILLISECONDS, scheduler); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); result.subscribe(ts); ts.cancel(); @@ -232,7 +234,7 @@ public void delaySubscriptionCancelBeforeTime() { @Test public void delayWithFlowableNormal1() { PublishProcessor source = PublishProcessor.create(); - final List> delays = new ArrayList>(); + final List> delays = new ArrayList<>(); final int n = 10; for (int i = 0; i < n; i++) { PublishProcessor delay = PublishProcessor.create(); @@ -577,7 +579,7 @@ public void delayWithFlowableReorder() { int n = 3; PublishProcessor source = PublishProcessor.create(); - final List> subjects = new ArrayList>(); + final List> subjects = new ArrayList<>(); for (int i = 0; i < n; i++) { subjects.add(PublishProcessor. create()); } @@ -625,7 +627,7 @@ public void accept(Notification t1) { } }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); delayed.subscribe(ts); // all will be delivered after 500ms since range does not delay between them scheduler.advanceTimeBy(500L, TimeUnit.MILLISECONDS); @@ -634,7 +636,7 @@ public void accept(Notification t1) { @Test public void backpressureWithTimedDelay() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, Flowable.bufferSize() * 2) .delay(100, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) @@ -662,7 +664,7 @@ public Integer apply(Integer t) { @Test public void backpressureWithSubscriptionTimedDelay() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, Flowable.bufferSize() * 2) .delaySubscription(100, TimeUnit.MILLISECONDS) .delay(100, TimeUnit.MILLISECONDS) @@ -691,7 +693,7 @@ public Integer apply(Integer t) { @Test public void backpressureWithSelectorDelay() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, Flowable.bufferSize() * 2) .delay(new Function>() { @@ -726,7 +728,7 @@ public Integer apply(Integer t) { @Test public void backpressureWithSelectorDelayAndSubscriptionDelay() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, Flowable.bufferSize() * 2) .delay(Flowable.defer(new Supplier>() { @@ -771,7 +773,7 @@ public void errorRunsBeforeOnNext() { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); pp.delay(1, TimeUnit.SECONDS, test).subscribe(ts); @@ -794,7 +796,7 @@ public void delaySupplierSimple() { Flowable source = Flowable.range(1, 5); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.delaySubscription(Flowable.defer(new Supplier>() { @Override @@ -820,7 +822,7 @@ public void delaySupplierCompletes() { Flowable source = Flowable.range(1, 5); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.delaySubscription(Flowable.defer(new Supplier>() { @Override @@ -847,7 +849,7 @@ public void delaySupplierErrors() { Flowable source = Flowable.range(1, 5); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.delaySubscription(Flowable.defer(new Supplier>() { @Override @@ -904,7 +906,7 @@ public void delaySubscriptionDisposeBeforeTime() { Flowable result = Flowable.just(1, 2, 3).delaySubscription(100, TimeUnit.MILLISECONDS, scheduler); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); result.subscribe(ts); ts.cancel(); @@ -918,7 +920,7 @@ public void delaySubscriptionDisposeBeforeTime() { @Test public void onErrorCalledOnScheduler() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference thread = new AtomicReference(); + final AtomicReference thread = new AtomicReference<>(); Flowable.error(new Exception()) .delay(0, TimeUnit.MILLISECONDS, Schedulers.newThread()) @@ -1030,4 +1032,38 @@ public Publisher apply(Integer t) throws Exception { .to(TestHelper.testConsumer()) .assertFailureAndMessage(NullPointerException.class, "The itemDelay returned a null Publisher"); } + + @Test + public void cancelShouldPreventRandomSubsequentEmissions() { + for (int attempt = 1; attempt < 100; attempt ++) { + + SequentialDisposable disposable = new SequentialDisposable(); + ConcurrentLinkedQueue sink = new ConcurrentLinkedQueue<>(); + + disposable.replace( + Flowable.range(1, 10) + .delay(1, TimeUnit.MICROSECONDS, Schedulers.computation(), true) + .doOnNext(v -> { + if (v == 1) { + Schedulers.computation().scheduleDirect(disposable::dispose); + } + sink.offer(v); + }) + .subscribe()); + + LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1)); + + Integer last = null; + + while (!sink.isEmpty()) { + Integer current = sink.poll(); + + if (last != null && last + 1 != current) { + fail("Emission hole: " + last + " -> " + current); + } + + last = current; + } + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java index 75a05cbd23..89a1454614 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDematerializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -141,7 +141,7 @@ public void completePassThru() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriberEx ts = new TestSubscriberEx(subscriber); + TestSubscriberEx ts = new TestSubscriberEx<>(subscriber); dematerialize.subscribe(ts); System.out.println(ts.errors()); @@ -223,7 +223,7 @@ protected void subscribeActual(Subscriber> subscrib } @Test - public void nonNotificationInstanceAfterDispose() { + public void notificationInstanceAfterDispose() { new Flowable>() { @Override protected void subscribeActual(Subscriber> subscriber) { @@ -236,4 +236,20 @@ protected void subscribeActual(Subscriber> subscrib .test() .assertResult(); } + + @Test + @SuppressWarnings("unchecked") + public void nonNotificationInstanceAfterDispose() { + new Flowable() { + @Override + protected void subscribeActual(Subscriber subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.onNext(Notification.createOnComplete()); + subscriber.onNext(1); + } + } + .dematerialize(v -> (Notification)v) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetachTest.java index c328565218..23d895ca08 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDetachTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.flowable; import java.lang.ref.WeakReference; @@ -35,9 +33,9 @@ public class FlowableDetachTest extends RxJavaTest { public void just() throws Exception { o = new Object(); - WeakReference wr = new WeakReference(o); + WeakReference wr = new WeakReference<>(o); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(o).count().toFlowable().onTerminateDetach().subscribe(ts); @@ -56,7 +54,7 @@ public void just() throws Exception { @Test public void error() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.error(new TestException()).onTerminateDetach().subscribe(ts); @@ -67,7 +65,7 @@ public void error() { @Test public void empty() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.empty().onTerminateDetach().subscribe(ts); @@ -78,7 +76,7 @@ public void empty() { @Test public void range() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 1000).onTerminateDetach().subscribe(ts); @@ -91,9 +89,9 @@ public void range() { public void backpressured() throws Exception { o = new Object(); - WeakReference wr = new WeakReference(o); + WeakReference wr = new WeakReference<>(o); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.just(o).count().toFlowable().onTerminateDetach().subscribe(ts); @@ -117,9 +115,9 @@ public void backpressured() throws Exception { public void justUnsubscribed() throws Exception { o = new Object(); - WeakReference wr = new WeakReference(o); + WeakReference wr = new WeakReference<>(o); - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.just(o).count().toFlowable().onTerminateDetach().subscribe(ts); @@ -135,9 +133,9 @@ public void justUnsubscribed() throws Exception { @Test public void deferredUpstreamProducer() { - final AtomicReference> subscriber = new AtomicReference>(); + final AtomicReference> subscriber = new AtomicReference<>(); - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.unsafeCreate(new Publisher() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctTest.java index 70682d9e03..362283c719 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,9 +27,10 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.UnicastProcessor; import io.reactivex.rxjava3.testsupport.*; @@ -128,13 +129,13 @@ public void fusedSync() { public void fusedAsync() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - us + up .distinct() .subscribe(ts); - TestHelper.emit(us, 1, 1, 2, 1, 3, 2, 4, 5, 4); + TestHelper.emit(up, 1, 1, 2, 1, 3, 2, 4, 5, 4); ts.assertFusionMode(QueueFuseable.ASYNC) .assertResult(1, 2, 3, 4, 5); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChangedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChangedTest.java index 9d9aa55fd8..00525b562b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChangedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDistinctUntilChangedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,8 +28,8 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNextTest.java index 51b27abad1..1e36eee49f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterNextTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,14 +23,14 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.processors.UnicastProcessor; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; public class FlowableDoAfterNextTest extends RxJavaTest { - final List values = new ArrayList(); + final List values = new ArrayList<>(); final Consumer afterNext = new Consumer() { @Override @@ -133,11 +133,6 @@ public void asyncFused() { assertEquals(Arrays.asList(-1, -2, -3, -4, -5), values); } - @Test(expected = NullPointerException.class) - public void consumerNull() { - Flowable.just(1).doAfterNext(null); - } - @Test public void justConditional() { Flowable.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterTerminateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterTerminateTest.java index 210ed3879b..ab1a3bd8ab 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterTerminateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoAfterTerminateTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.flowable; import static org.junit.Assert.*; @@ -91,7 +89,7 @@ public void ifFinallyActionThrowsExceptionShouldNotBeSwallowedAndActionShouldBeC Action finallyAction = Mockito.mock(Action.class); doThrow(new IllegalStateException()).when(finallyAction).run(); - TestSubscriber testSubscriber = new TestSubscriber(); + TestSubscriber testSubscriber = new TestSubscriber<>(); Flowable .just("value") diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java index 7662162d22..79cba18b3d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoFinallyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,8 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.UnicastProcessor; import io.reactivex.rxjava3.testsupport.*; @@ -208,7 +209,7 @@ public void syncFusedConditional() { Flowable.range(1, 5) .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.SYNC) @@ -237,7 +238,7 @@ public void nonFusedConditional() { Flowable.range(1, 5).hide() .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.NONE) @@ -252,7 +253,7 @@ public void syncFusedBoundaryConditional() { Flowable.range(1, 5) .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.NONE) @@ -270,7 +271,7 @@ public void asyncFusedConditional() { up .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.ASYNC) @@ -288,7 +289,7 @@ public void asyncFusedBoundaryConditional() { up .doFinally(this) - .filter(Functions.alwaysTrue()) + .compose(TestHelper.conditional()) .subscribe(ts); ts.assertFusionMode(QueueFuseable.NONE) @@ -297,11 +298,6 @@ public void asyncFusedBoundaryConditional() { assertEquals(1, calls); } - @Test(expected = NullPointerException.class) - public void nullAction() { - Flowable.just(1).doFinally(null); - } - @Test public void actionThrows() { List errors = TestHelper.trackPluginErrors(); @@ -440,7 +436,7 @@ public void onComplete() { @Test public void eventOrdering() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.error(new TestException()) .doOnCancel(new Action() { @@ -480,7 +476,7 @@ public void run() throws Exception { @Test public void eventOrdering2() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.just(1) .doOnCancel(new Action() { @@ -517,4 +513,31 @@ public void run() throws Exception { assertEquals(Arrays.asList("onNext", "onComplete", "finally"), list); } + + @Test + public void fusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + TestHelper.rejectFlowableFusion() + .doFinally(() -> { }) + .subscribeWith(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } + + @Test + public void fusionRejectedConditional() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ANY); + + TestHelper.rejectFlowableFusion() + .doFinally(() -> { }) + .compose(TestHelper.conditional()) + .subscribeWith(ts); + + ts.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEachTest.java index deea77ea70..484d62c19e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,8 +29,9 @@ import io.reactivex.rxjava3.flowables.ConnectableFlowable; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.UnicastProcessor; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -173,7 +174,7 @@ public void accept(List booleans) { @Test public void onErrorThrows() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.error(new TestException()) .doOnError(new Consumer() { @@ -691,7 +692,7 @@ public void run() throws Exception { @Test public void dispose() { - TestHelper.checkDisposed(Flowable.just(1).doOnEach(new TestSubscriber())); + TestHelper.checkDisposed(Flowable.just(1).doOnEach(new TestSubscriber<>())); } @Test @@ -699,7 +700,7 @@ public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowable(new Function, Flowable>() { @Override public Flowable apply(Flowable f) throws Exception { - return f.doOnEach(new TestSubscriber()); + return f.doOnEach(new TestSubscriber<>()); } }); } @@ -736,7 +737,7 @@ public void doOnNextDoOnErrorCombinedFused() { .compose(new FlowableTransformer() { @Override public Publisher apply(Flowable v) { - return new FlowableDoOnEach(v, + return new FlowableDoOnEach<>(v, new Consumer() { @Override public void accept(Integer v) throws Exception { @@ -752,7 +753,7 @@ public void accept(Throwable e) throws Exception { Functions.EMPTY_ACTION , Functions.EMPTY_ACTION - ); + ); } }) .publish(); @@ -866,7 +867,7 @@ public void doOnNextDoOnErrorCombinedFusedConditional() { .compose(new FlowableTransformer() { @Override public Publisher apply(Flowable v) { - return new FlowableDoOnEach(v, + return new FlowableDoOnEach<>(v, new Consumer() { @Override public void accept(Integer v) throws Exception { @@ -882,7 +883,7 @@ public void accept(Throwable e) throws Exception { Functions.EMPTY_ACTION , Functions.EMPTY_ACTION - ); + ); } }) .filter(Functions.alwaysTrue()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycleTest.java index 524a0b5acb..e1df09d7ee 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnLifecycleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnRequestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnRequestTest.java index e4eb8543f6..3e123089dc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnRequestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnRequestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -51,7 +51,7 @@ public void accept(long n) { @Test public void doRequest() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.range(1, 5) // .doOnRequest(new LongConsumer() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnSubscribeTest.java index f300d0427f..6dff7a5321 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnSubscribeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnSubscribeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -66,7 +66,7 @@ public void doOnUnSubscribeWorksWithRefCount() throws Exception { final AtomicInteger onSubscribed = new AtomicInteger(); final AtomicInteger countBefore = new AtomicInteger(); final AtomicInteger countAfter = new AtomicInteger(); - final AtomicReference> sref = new AtomicReference>(); + final AtomicReference> sref = new AtomicReference<>(); Flowable f = Flowable.unsafeCreate(new Publisher() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnUnsubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnUnsubscribeTest.java index 340b3f22d9..d060928dee 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnUnsubscribeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnUnsubscribeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -67,12 +67,12 @@ public void run() { } }); - List subscriptions = new ArrayList(); - List> subscribers = new ArrayList>(); + List subscriptions = new ArrayList<>(); + List> subscribers = new ArrayList<>(); for (int i = 0; i < subCount; ++i) { - TestSubscriber subscriber = new TestSubscriber(); - subscriptions.add(Disposables.fromSubscription(subscriber)); + TestSubscriber subscriber = new TestSubscriber<>(); + subscriptions.add(Disposable.fromSubscription(subscriber)); longs.subscribe(subscriber); subscribers.add(subscriber); } @@ -128,13 +128,13 @@ public void run() { .publish() .refCount(); - List subscriptions = new ArrayList(); - List> subscribers = new ArrayList>(); + List subscriptions = new ArrayList<>(); + List> subscribers = new ArrayList<>(); for (int i = 0; i < subCount; ++i) { - TestSubscriber subscriber = new TestSubscriber(); + TestSubscriber subscriber = new TestSubscriber<>(); longs.subscribe(subscriber); - subscriptions.add(Disposables.fromSubscription(subscriber)); + subscriptions.add(Disposable.fromSubscription(subscriber)); subscribers.add(subscriber); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtTest.java index f4701665cb..0e36e075a8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableElementAtTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,13 +17,13 @@ import java.util.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; @@ -72,7 +72,7 @@ public void elementAt() { @Test public void elementAtConstrainsUpstreamRequests() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.fromArray(1, 2, 3, 4) .doOnRequest(new LongConsumer() { @Override @@ -88,7 +88,7 @@ public void accept(long n) throws Throwable { @Test public void elementAtWithDefaultConstrainsUpstreamRequests() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.fromArray(1, 2, 3, 4) .doOnRequest(new LongConsumer() { @Override @@ -344,7 +344,7 @@ public void badSourceObservable() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilterTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilterTest.java index 38e94571c2..4b62f1e2a4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilterTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFilterTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,8 +28,9 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -158,7 +159,7 @@ public void functionCrashUnsubscribes() { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); pp.filter(new Predicate() { @Override @@ -557,9 +558,9 @@ public boolean test(Integer v) throws Exception { public void fusedAsync() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - us + up .filter(new Predicate() { @Override public boolean test(Integer v) throws Exception { @@ -568,7 +569,7 @@ public boolean test(Integer v) throws Exception { }) .subscribe(ts); - TestHelper.emit(us, 1, 2, 3, 4, 5); + TestHelper.emit(up, 1, 2, 3, 4, 5); ts.assertFusionMode(QueueFuseable.ASYNC) .assertResult(2, 4); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFirstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFirstTest.java index efcdb290c0..b6100ddeb3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFirstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFirstTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java index 592756d85e..5e4e5c8f77 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,18 +16,19 @@ import static org.junit.Assert.*; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import org.junit.Test; import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -455,7 +456,7 @@ public CompletableSource apply(Integer v) throws Exception { return new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -494,7 +495,7 @@ public CompletableSource apply(Integer v) throws Exception { return new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -510,6 +511,23 @@ protected void subscribeActual(CompletableObserver observer) { @Test public void delayErrorMaxConcurrency() { + Flowable.range(1, 3) + .flatMapCompletable(new Function() { + @Override + public CompletableSource apply(Integer v) throws Exception { + if (v == 2) { + return Completable.error(new TestException()); + } + return Completable.complete(); + } + }, true, 1) + .toFlowable() + .test() + .assertFailure(TestException.class); + } + + @Test + public void delayErrorMaxConcurrencyCompletable() { Flowable.range(1, 3) .flatMapCompletable(new Function() { @Override @@ -570,4 +588,59 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.flatMapCompletable(v -> Completable.never()).toFlowable()); + } + + @Test + public void doubleOnSubscribeCompletable() { + TestHelper.checkDoubleOnSubscribeFlowableToCompletable(f -> f.flatMapCompletable(v -> Completable.never())); + } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor pp1 = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); + CountDownLatch cdl = new CountDownLatch(1); + + pp1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + ts.cancel(); + }, cdl); + return Completable.complete(); + }) + .toFlowable() + .subscribe(ts); + + pp1.onNext(1); + + cdl.await(); + } + } + + @Test + public void cancelWhileMappingCompletable() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor pp1 = PublishProcessor.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + pp1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Completable.complete(); + }) + .subscribe(to); + + pp1.onNext(1); + + cdl.await(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java index 332d906952..a377761f0c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -30,6 +30,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -442,7 +443,7 @@ public void badInnerSource() { .flatMapMaybe(Functions.justFunction(new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -523,7 +524,7 @@ public MaybeSource apply(PublishProcessor v) throws Exception @Test public void disposeInner() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).flatMapMaybe(new Function>() { @Override @@ -531,7 +532,7 @@ public MaybeSource apply(Integer v) throws Exception { return new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -641,4 +642,57 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().flatMapMaybe(v -> Maybe.never())); + } + + @Test + public void successRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + MaybeSubject ss1 = MaybeSubject.create(); + MaybeSubject ss2 = MaybeSubject.create(); + + TestSubscriber ts = Flowable.just(ss1, ss2).flatMapMaybe(v -> v) + .test(); + + TestHelper.race( + () -> ss1.onSuccess(1), + () -> ss2.onSuccess(1) + ); + + ts.assertResult(1, 1); + } + } + + @Test + public void successCompleteRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + MaybeSubject ss1 = MaybeSubject.create(); + MaybeSubject ss2 = MaybeSubject.create(); + + TestSubscriber ts = Flowable.just(ss1, ss2).flatMapMaybe(v -> v) + .test(); + + TestHelper.race( + () -> ss1.onSuccess(1), + () -> ss2.onComplete() + ); + + ts.assertResult(1); + } + } + + @Test + public void successShortcut() { + MaybeSubject ss1 = MaybeSubject.create(); + + TestSubscriber ts = Flowable.just(ss1).hide().flatMapMaybe(v -> v) + .test(); + + ss1.onSuccess(1); + + ts.assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java index e207dc57a1..b13e8316af 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -30,6 +30,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.SingleSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -361,7 +362,7 @@ public void badInnerSource() { .flatMapSingle(Functions.justFunction(new Single() { @Override protected void subscribeActual(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -408,7 +409,7 @@ public SingleSource apply(PublishProcessor v) throws Exception @Test public void disposeInner() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).flatMapSingle(new Function>() { @Override @@ -416,7 +417,7 @@ public SingleSource apply(Integer v) throws Exception { return new Single() { @Override protected void subscribeActual(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -541,4 +542,39 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().flatMapSingle(v -> Single.never())); + } + + @Test + public void successRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + SingleSubject ss1 = SingleSubject.create(); + SingleSubject ss2 = SingleSubject.create(); + + TestSubscriber ts = Flowable.just(ss1, ss2).flatMapSingle(v -> v) + .test(); + + TestHelper.race( + () -> ss1.onSuccess(1), + () -> ss2.onSuccess(1) + ); + + ts.assertResult(1, 1); + } + } + + @Test + public void successShortcut() { + SingleSubject ss1 = SingleSubject.create(); + + TestSubscriber ts = Flowable.just(ss1).hide().flatMapSingle(v -> v) + .test(); + + ss1.onSuccess(1); + + ts.assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java index 483d92062a..be94c5e57c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlatMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,19 +17,22 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.*; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -343,13 +346,13 @@ public Flowable apply(Integer t1) { } }, m); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertNoErrors(); - Set expected = new HashSet(Arrays.asList( + Set expected = new HashSet<>(Arrays.asList( 10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61, 70, 71, 80, 81, 90, 91, 100, 101 )); Assert.assertEquals(expected.size(), ts.values().size()); @@ -374,13 +377,13 @@ public Integer apply(Integer t1, Integer t2) { } }, m); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertNoErrors(); - Set expected = new HashSet(Arrays.asList( + Set expected = new HashSet<>(Arrays.asList( 1010, 1011, 2020, 2021, 3030, 3031, 4040, 4041, 5050, 5051, 6060, 6061, 7070, 7071, 8080, 8081, 9090, 9091, 10100, 10101 )); @@ -420,7 +423,7 @@ public void flatMapTransformsMaxConcurrentNormal() { Flowable source = Flowable.fromIterable(Arrays.asList(10, 20, 30)); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriberEx ts = new TestSubscriberEx(subscriber); + TestSubscriberEx ts = new TestSubscriberEx<>(subscriber); Function> just = just(onNext); Function> just2 = just(onError); @@ -447,7 +450,7 @@ public void flatMapRangeMixedAsyncLoop() { if (i % 10 == 0) { System.out.println("flatMapRangeAsyncLoop > " + i); } - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(0, 1000) .flatMap(new Function>() { final Random rnd = new Random(); @@ -471,7 +474,7 @@ public Flowable apply(Integer t) { ts.assertNoErrors(); List list = ts.values(); if (list.size() < 1000) { - Set set = new HashSet(list); + Set set = new HashSet<>(list); for (int j = 0; j < 1000; j++) { if (!set.contains(j)) { System.out.println(j + " missing"); @@ -485,7 +488,7 @@ public Flowable apply(Integer t) { @Test public void flatMapIntPassthruAsync() { for (int i = 0; i < 1000; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 1000).flatMap(new Function>() { @Override @@ -504,7 +507,7 @@ public Flowable apply(Integer t) { @Test public void flatMapTwoNestedSync() { for (final int n : new int[] { 1, 1000, 1000000 }) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2).flatMap(new Function>() { @Override @@ -1116,4 +1119,387 @@ public Publisher apply(Integer v) throws Throwable { } }); } + + @Test + public void mainErrorsInnerCancelled() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + pp1 + .flatMap(v -> pp2) + .test(); + + pp1.onNext(1); + assertTrue("No subscribers?", pp2.hasSubscribers()); + + pp1.onError(new TestException()); + + assertFalse("Has subscribers?", pp2.hasSubscribers()); + } + + @Test + public void innerErrorsMainCancelled() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + pp1 + .flatMap(v -> pp2) + .test(); + + pp1.onNext(1); + assertTrue("No subscribers?", pp2.hasSubscribers()); + + pp2.onError(new TestException()); + + assertFalse("Has subscribers?", pp1.hasSubscribers()); + } + + @Test + public void innerIsDisposed() { + FlowableFlatMap.InnerSubscriber inner = new FlowableFlatMap.InnerSubscriber<>(null, 10, 0L); + + assertFalse(inner.isDisposed()); + + inner.dispose(); + + assertTrue(inner.isDisposed()); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().flatMap(v -> Flowable.never())); + } + + @Test + public void signalsAfterMapperCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onNext(2); + s.onComplete(); + s.onError(new IOException()); + } + } + .flatMap(v -> { + throw new TestException(); + }) + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void scalarQueueTerminate() { + PublishProcessor pp = PublishProcessor.create(); + TestSubscriber ts = new TestSubscriber<>(); + + pp + .flatMap(v -> Flowable.just(v)) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onNext(3); + } + }) + .take(2) + .subscribe(ts); + + pp.onNext(1); + + ts.assertResult(1, 2); + } + + @Test + public void scalarQueueCompleteMain() throws Exception { + PublishProcessor pp = PublishProcessor.create(); + TestSubscriber ts = new TestSubscriber<>(); + CountDownLatch cdl = new CountDownLatch(1); + pp + .flatMap(v -> Flowable.just(v)) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + TestHelper.raceOther(() -> pp.onComplete(), cdl); + } + }) + .subscribe(ts); + + pp.onNext(1); + + cdl.await(); + ts.assertResult(1, 2); + } + + @Test + public void fusedInnerCrash() { + UnicastProcessor up = UnicastProcessor.create(); + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = Flowable.just( + pp, + up.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + up.onNext(10); + } + }) + .test(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertFailure(TestException.class, 1, 2); + } + + @Test + public void fusedInnerCrash2() { + UnicastProcessor up = UnicastProcessor.create(); + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = Flowable.just( + up.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + , pp + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + up.onNext(10); + } + }) + .test(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertFailure(TestException.class, 1, 2); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.flatMap(v -> Flowable.never())); + } + + @Test + public void allConcurrency() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2).hide(), Integer.MAX_VALUE) + .test() + .assertResult(2); + } + + @Test + public void allConcurrencyScalarInner() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), Integer.MAX_VALUE) + .test() + .assertResult(2); + } + + @Test + public void allConcurrencyScalarInnerEmpty() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.empty(), Integer.MAX_VALUE) + .test() + .assertResult(); + } + + static final class ScalarEmptyCancel extends Flowable implements Supplier { + final TestSubscriber ts; + + ScalarEmptyCancel(TestSubscriber ts) { + this.ts = ts; + } + + @Override + public @NonNull Integer get() throws Throwable { + ts.cancel(); + return null; + } + + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + EmptySubscription.complete(subscriber); + } + } + + @Test + public void someConcurrencyScalarInnerCancel() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .hide() + .flatMap(v -> new ScalarEmptyCancel(ts)) + .subscribeWith(ts) + .assertEmpty(); + } + + @Test + public void allConcurrencyBackpressured() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), Integer.MAX_VALUE) + .test(0L) + .assertEmpty() + .requestMore(1) + .assertResult(2); + } + + @Test + public void someConcurrencyInnerScalarCancel() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), 2) + .takeUntil(v -> true) + .test() + .assertResult(2); + } + + @Test + public void scalarInnerOuterOverflow() { + new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + } + } + .flatMap(v -> Flowable.just(v), 1) + .test(0L) + .assertFailure(QueueOverflowException.class); + } + + @Test + public void scalarInnerOuterOverflowSlowPath() { + AtomicReference> ref = new AtomicReference<>(); + new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + ref.set(subscriber); + subscriber.onNext(1); + } + } + .flatMap(v -> Flowable.just(v), 1) + .doOnNext(v -> { + if (v == 1) { + ref.get().onNext(2); + ref.get().onNext(3); + } + }) + .test() + .assertFailure(QueueOverflowException.class, 1); + } + + @Test + public void innerFastPathEmitOverflow() { + Flowable.just(1) + .hide() + .flatMap(v -> new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + } + }, false, 1, 1) + .test(0L) + .assertFailure(QueueOverflowException.class); + } + + @Test + public void takeFromScalarQueue() { + Flowable.just(1) + .hide() + .flatMap(v -> Flowable.just(2), 2) + .takeUntil(v -> true) + .test(0L) + .requestMore(2) + .assertResult(2); + } + + @Test + public void scalarInnerQueueEmpty() { + Flowable.just(1) + .concatWith(Flowable.never()) + .hide() + .flatMap(v -> Flowable.just(2), 2) + .test(0L) + .requestMore(2) + .assertValuesOnly(2); + } + + @Test + public void innerCompletesAfterOnNextInDrainThenCancels() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(0L); + + Flowable.just(1) + .hide() + .flatMap(v -> pp) + .doOnNext(v -> { + if (v == 1) { + pp.onComplete(); + ts.cancel(); + } + }) + .subscribe(ts); + + pp.onNext(1); + + ts + .requestMore(1) + .assertValuesOnly(1); + } + + @Test(timeout = 5000) + public void mixedScalarAsync() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Flowable + .range(0, 20) + .flatMap( + integer -> { + if (integer % 5 != 0) { + return Flowable + .just(integer); + } + + return Flowable + .just(-integer) + .observeOn(Schedulers.computation()); + }, + false, + 1 + ) + .ignoreElements() + .blockingAwait(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterableTest.java index e9e25c8af2..2cb9392e19 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFlattenIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,10 +26,11 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.flowable.FlowableFlattenIterable.FlattenIterableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -40,7 +41,7 @@ public class FlowableFlattenIterableTest extends RxJavaTest { @Test public void normal0() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 2) .reduce(new BiFunction() { @@ -72,7 +73,7 @@ public Iterable apply(Integer v) { @Test public void normal() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5).concatMapIterable(mapper) .subscribe(ts); @@ -84,7 +85,7 @@ public void normal() { @Test public void normalViaFlatMap() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5).flatMapIterable(mapper) .subscribe(ts); @@ -96,7 +97,7 @@ public void normalViaFlatMap() { @Test public void normalBackpressured() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.range(1, 5).concatMapIterable(mapper) .subscribe(ts); @@ -126,7 +127,7 @@ public void normalBackpressured() { @Test public void longRunning() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); int n = 1000 * 1000; @@ -140,7 +141,7 @@ public void longRunning() { @Test public void asIntermediate() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); int n = 1000 * 1000; @@ -159,7 +160,7 @@ public Flowable apply(Integer v) { @Test public void just() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).concatMapIterable(mapper) .subscribe(ts); @@ -171,7 +172,7 @@ public void just() { @Test public void justHidden() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).hide().concatMapIterable(mapper) .subscribe(ts); @@ -183,7 +184,7 @@ public void justHidden() { @Test public void empty() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.empty().concatMapIterable(mapper) .subscribe(ts); @@ -195,7 +196,7 @@ public void empty() { @Test public void error() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).concatWith(Flowable.error(new TestException())) .concatMapIterable(mapper) @@ -208,7 +209,7 @@ public void error() { @Test public void iteratorHasNextThrowsImmediately() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Iterable it = new Iterable() { @Override @@ -248,7 +249,7 @@ public Iterable apply(Integer v) { @Test public void iteratorHasNextThrowsImmediatelyJust() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Iterable it = new Iterable() { @Override @@ -288,7 +289,7 @@ public Iterable apply(Integer v) { @Test public void iteratorHasNextThrowsSecondCall() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Iterable it = new Iterable() { @Override @@ -332,7 +333,7 @@ public Iterable apply(Integer v) { @Test public void iteratorNextThrows() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Iterable it = new Iterable() { @Override @@ -372,7 +373,7 @@ public Iterable apply(Integer v) { @Test public void iteratorNextThrowsAndUnsubscribes() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Iterable it = new Iterable() { @Override @@ -418,7 +419,7 @@ public Iterable apply(Integer v) { @Test public void mixture() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, 1000) .concatMapIterable(new Function>() { @@ -436,7 +437,7 @@ public Iterable apply(Integer v) { @Test public void emptyInnerThenSingleBackpressured() { - TestSubscriber ts = new TestSubscriber(1); + TestSubscriber ts = new TestSubscriber<>(1); Flowable.range(1, 2) .concatMapIterable(new Function>() { @@ -454,7 +455,7 @@ public Iterable apply(Integer v) { @Test public void manyEmptyInnerThenSingleBackpressured() { - TestSubscriber ts = new TestSubscriber(1); + TestSubscriber ts = new TestSubscriber<>(1); Flowable.range(1, 1000) .concatMapIterable(new Function>() { @@ -472,7 +473,7 @@ public Iterable apply(Integer v) { @Test public void hasNextIsNotCalledAfterChildUnsubscribedOnNext() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger counter = new AtomicInteger(); @@ -523,7 +524,7 @@ public Iterable apply(Integer v) { @Test public void normalPrefetchViaFlatMap() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5).flatMapIterable(mapper, 2) .subscribe(ts); @@ -813,7 +814,7 @@ protected void subscribeActual(Subscriber s) { } .flatMapIterable(Functions.justFunction(Arrays.asList(1)), 1) .test(0L) - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); } @Test @@ -827,7 +828,7 @@ public void oneByOne() { @Test public void cancelAfterHasNext() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 3).hide() .flatMapIterable(new Function>() { @@ -974,8 +975,8 @@ public Publisher apply(Flowable f) @Test public void upstreamFusionRejected() { - TestSubscriber ts = new TestSubscriber(); - FlattenIterableSubscriber f = new FlattenIterableSubscriber(ts, + TestSubscriber ts = new TestSubscriber<>(); + FlattenIterableSubscriber f = new FlattenIterableSubscriber<>(ts, Functions.justFunction(Collections.emptyList()), 128); final AtomicLong requested = new AtomicLong(); @@ -1031,8 +1032,8 @@ public void cancel() { public void onErrorLate() { List errors = TestHelper.trackPluginErrors(); try { - TestSubscriberEx ts = new TestSubscriberEx(); - FlattenIterableSubscriber f = new FlattenIterableSubscriber(ts, + TestSubscriberEx ts = new TestSubscriberEx<>(); + FlattenIterableSubscriber f = new FlattenIterableSubscriber<>(ts, Functions.justFunction(Collections.emptyList()), 128); f.onSubscribe(new BooleanSubscription()); @@ -1059,8 +1060,8 @@ public void badRequest() { @Test public void fusedCurrentIteratorEmpty() throws Throwable { - TestSubscriber ts = new TestSubscriber(0); - FlattenIterableSubscriber f = new FlattenIterableSubscriber(ts, + TestSubscriber ts = new TestSubscriber<>(0); + FlattenIterableSubscriber f = new FlattenIterableSubscriber<>(ts, Functions.justFunction(Arrays.asList(1, 2)), 128); f.onSubscribe(new BooleanSubscription()); @@ -1080,8 +1081,8 @@ public void fusedCurrentIteratorEmpty() throws Throwable { @Test public void fusionRequestedState() throws Exception { - TestSubscriber ts = new TestSubscriber(0); - FlattenIterableSubscriber f = new FlattenIterableSubscriber(ts, + TestSubscriber ts = new TestSubscriber<>(0); + FlattenIterableSubscriber f = new FlattenIterableSubscriber<>(ts, Functions.justFunction(Arrays.asList(1, 2)), 128); f.onSubscribe(new BooleanSubscription()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java index c643c0652b..e74d5caf26 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableForEachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,12 +22,13 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.testsupport.TestHelper; public class FlowableForEachTest extends RxJavaTest { @Test public void forEachWile() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5) .doOnNext(new Consumer() { @@ -48,7 +49,7 @@ public boolean test(Integer v) throws Exception { @Test public void forEachWileWithError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) .doOnNext(new Consumer() { @@ -72,4 +73,11 @@ public void accept(Throwable e) throws Exception { assertEquals(Arrays.asList(1, 2, 3, 4, 5, 100), list); } + @Test + public void dispose() { + TestHelper.checkDisposed( + Flowable.never() + .forEachWhile(v -> true) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromActionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromActionTest.java new file mode 100644 index 0000000000..5395ae3068 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromActionTest.java @@ -0,0 +1,214 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class FlowableFromActionTest extends RxJavaTest { + @Test + public void fromAction() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Flowable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromActionTwice() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Action run = new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }; + + Flowable.fromAction(run) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + + Flowable.fromAction(run) + .test() + .assertResult(); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void fromActionInvokesLazy() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Flowable source = Flowable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }); + + assertEquals(0, atomicInteger.get()); + + source + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromActionThrows() { + Flowable.fromAction(new Action() { + @Override + public void run() throws Exception { + throw new UnsupportedOperationException(); + } + }) + .test() + .assertFailure(UnsupportedOperationException.class); + } + + @SuppressWarnings("unchecked") + @Test + public void callable() throws Throwable { + final int[] counter = { 0 }; + + Flowable m = Flowable.fromAction(new Action() { + @Override + public void run() throws Exception { + counter[0]++; + } + }); + + assertTrue(m.getClass().toString(), m instanceof Supplier); + + assertNull(((Supplier)m).get()); + + assertEquals(1, counter[0]); + } + + @Test + public void noErrorLoss() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + TestSubscriber ts = Flowable.fromAction(new Action() { + @Override + public void run() throws Exception { + cdl1.countDown(); + cdl2.await(5, TimeUnit.SECONDS); + } + }).subscribeOn(Schedulers.single()).test(); + + assertTrue(cdl1.await(5, TimeUnit.SECONDS)); + + ts.cancel(); + + int timeout = 10; + + while (timeout-- > 0 && errors.isEmpty()) { + Thread.sleep(100); + } + + TestHelper.assertUndeliverable(errors, 0, InterruptedException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void disposedUpfront() throws Throwable { + Action run = mock(Action.class); + + Flowable.fromAction(run) + .test(1L, true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void cancelWhileRunning() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromAction(new Action() { + @Override + public void run() throws Exception { + ts.cancel(); + } + }) + .subscribeWith(ts) + .assertEmpty(); + + assertTrue(ts.isCancelled()); + } + + @Test + public void asyncFused() throws Throwable { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ASYNC); + + Action action = mock(Action.class); + + Flowable.fromAction(action) + .subscribe(ts); + + ts.assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + + verify(action).run(); + } + + @Test + public void syncFusedRejected() throws Throwable { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Action action = mock(Action.class); + + Flowable.fromAction(action) + .subscribe(ts); + + ts.assertFusionMode(QueueFuseable.NONE) + .assertResult(); + + verify(action).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArrayTest.java index 62607299d6..d3195ad8b2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromArrayTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; @@ -21,7 +18,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Predicate; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.operators.ScalarSupplier; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -37,7 +34,7 @@ Flowable create(int n) { @Test public void simple() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); create(1000).subscribe(ts); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallableTest.java index cc4b1b64bb..b603fd2d9b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCallableTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; @@ -121,7 +118,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber outer = new TestSubscriber(subscriber); + TestSubscriber outer = new TestSubscriber<>(subscriber); fromCallableFlowable .subscribeOn(Schedulers.computation()) @@ -248,7 +245,7 @@ public Object call() throws Exception { public void undeliverableUponCancellation() throws Exception { List errors = TestHelper.trackPluginErrors(); try { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.fromCallable(new Callable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java new file mode 100644 index 0000000000..fc8e4ec977 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromCompletableTest.java @@ -0,0 +1,204 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class FlowableFromCompletableTest extends RxJavaTest { + @Test + public void fromCompletable() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Flowable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + })) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromCompletableTwice() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Action run = new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }; + + Flowable.fromCompletable(Completable.fromAction(run)) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + + Flowable.fromCompletable(Completable.fromAction(run)) + .test() + .assertResult(); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void fromCompletableInvokesLazy() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Flowable source = Flowable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + })); + + assertEquals(0, atomicInteger.get()); + + source + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromCompletableThrows() { + Flowable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + throw new UnsupportedOperationException(); + } + })) + .test() + .assertFailure(UnsupportedOperationException.class); + } + + @Test + public void noErrorLoss() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + TestSubscriber ts = Flowable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + cdl1.countDown(); + cdl2.await(5, TimeUnit.SECONDS); + } + })) + .subscribeOn(Schedulers.single()).test(); + + assertTrue(cdl1.await(5, TimeUnit.SECONDS)); + + ts.cancel(); + + int timeout = 10; + + while (timeout-- > 0 && errors.isEmpty()) { + Thread.sleep(100); + } + + TestHelper.assertUndeliverable(errors, 0, InterruptedException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void disposedUpfront() throws Throwable { + Action run = mock(Action.class); + + Flowable.fromCompletable(Completable.fromAction(run)) + .test(1L, true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void cancelWhileRunning() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + ts.cancel(); + } + })) + .subscribeWith(ts) + .assertEmpty(); + + assertTrue(ts.isCancelled()); + } + + @Test + public void asyncFused() throws Throwable { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ASYNC); + + Action action = mock(Action.class); + + Flowable.fromCompletable(Completable.fromAction(action)) + .subscribe(ts); + + ts.assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + + verify(action).run(); + } + + @Test + public void syncFusedRejected() throws Throwable { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Action action = mock(Action.class); + + Flowable.fromCompletable(Completable.fromAction(action)) + .subscribe(ts); + + ts.assertFusionMode(QueueFuseable.NONE) + .assertResult(); + + verify(action).run(); + } + + @Test + public void upstream() { + Flowable f = Flowable.fromCompletable(Completable.never()); + assertTrue(f instanceof HasUpstreamCompletableSource); + assertSame(Completable.never(), ((HasUpstreamCompletableSource)f).source()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java index 1b21baf610..5fe804a310 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,18 +19,21 @@ import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.*; import org.junit.Test; import org.mockito.Mockito; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.*; @@ -38,11 +41,6 @@ public class FlowableFromIterableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void nullValue() { - Flowable.fromIterable(null); - } - @Test public void listIterable() { Flowable flowable = Flowable.fromIterable(Arrays. asList("one", "two", "three")); @@ -119,13 +117,13 @@ public void observableFromIterable() { @Test public void backpressureViaRequest() { - ArrayList list = new ArrayList(Flowable.bufferSize()); + ArrayList list = new ArrayList<>(Flowable.bufferSize()); for (int i = 1; i <= Flowable.bufferSize() + 1; i++) { list.add(i); } Flowable f = Flowable.fromIterable(list); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.assertNoValues(); ts.request(1); @@ -145,7 +143,7 @@ public void backpressureViaRequest() { public void noBackpressure() { Flowable f = Flowable.fromIterable(Arrays.asList(1, 2, 3, 4, 5)); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.assertNoValues(); ts.request(Long.MAX_VALUE); // infinite @@ -161,7 +159,7 @@ public void subscribeMultipleTimes() { Flowable f = Flowable.fromIterable(Arrays.asList(1, 2, 3)); for (int i = 0; i < 10; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); f.subscribe(ts); @@ -333,7 +331,7 @@ public Iterator iterator() { } }; - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.fromIterable(it).subscribe(ts); @@ -366,7 +364,7 @@ public void remove() { } }; - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.fromIterable(it).subscribe(ts); @@ -403,7 +401,7 @@ public void remove() { } }; - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.fromIterable(it).subscribe(ts); @@ -440,7 +438,7 @@ public void remove() { } }; - TestSubscriber ts = new TestSubscriber(5); + TestSubscriber ts = new TestSubscriber<>(5); Flowable.fromIterable(it).subscribe(ts); @@ -473,7 +471,7 @@ public void remove() { } }; - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.fromIterable(it).subscribe(ts); @@ -506,7 +504,7 @@ public void remove() { } }; - TestSubscriber ts = new TestSubscriber(5); + TestSubscriber ts = new TestSubscriber<>(5); Flowable.fromIterable(it).subscribe(ts); @@ -539,7 +537,7 @@ public void remove() { } }; - TestSubscriber ts = new TestSubscriber(5); + TestSubscriber ts = new TestSubscriber<>(5); ts.cancel(); Flowable.fromIterable(it).subscribe(ts); @@ -552,7 +550,7 @@ public void remove() { @Test public void fusionWithConcatMap() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.fromIterable(Arrays.asList(1, 2, 3, 4)).concatMap( new Function>() { @@ -724,7 +722,7 @@ public void normalConditionalLong2() { @Test public void requestRaceConditional() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Runnable r = new Runnable() { @Override @@ -744,7 +742,7 @@ public void run() { @Test public void requestRaceConditional2() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Runnable r = new Runnable() { @Override @@ -764,7 +762,7 @@ public void run() { @Test public void requestCancelConditionalRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Runnable r1 = new Runnable() { @Override @@ -791,7 +789,7 @@ public void run() { @Test public void requestCancelConditionalRace2() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Runnable r1 = new Runnable() { @Override @@ -818,7 +816,7 @@ public void run() { @Test public void requestCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Runnable r1 = new Runnable() { @Override @@ -844,7 +842,7 @@ public void run() { @Test public void requestCancelRace2() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Runnable r1 = new Runnable() { @Override @@ -933,7 +931,159 @@ public void hasNext2Throws() { @Test public void hasNextCancels() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesFastPath() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesSlowPath() { + final TestSubscriber ts = new TestSubscriber<>(10L); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesFastPathConditional() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + int count; + + @Override + public boolean hasNext() { + if (++count == 2) { + ts.cancel(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .filter(v -> true) + .subscribe(ts); + + ts.assertValue(1) + .assertNoErrors() + .assertNotComplete(); + } + + @Test + public void hasNextCancelsAndCompletesSlowPathConditional() { + final TestSubscriber ts = new TestSubscriber<>(10); Flowable.fromIterable(new Iterable() { @Override @@ -945,6 +1095,7 @@ public Iterator iterator() { public boolean hasNext() { if (++count == 2) { ts.cancel(); + return false; } return true; } @@ -961,10 +1112,112 @@ public void remove() { }; } }) + .filter(v -> true) .subscribe(ts); ts.assertValue(1) .assertNoErrors() .assertNotComplete(); } + + @Test + public void fusedPoll() throws Throwable { + AtomicReference> queue = new AtomicReference<>(); + + Flowable.fromIterable(Arrays.asList(1)) + .subscribe(new FlowableSubscriber() { + @Override + public void onSubscribe(@NonNull Subscription s) { + queue.set((SimpleQueue)s); + ((QueueSubscription)s).requestFusion(QueueFuseable.ANY); + } + + @Override + public void onNext(Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + + SimpleQueue q = queue.get(); + + assertFalse(q.isEmpty()); + + assertEquals(1, q.poll()); + + assertTrue(q.isEmpty()); + + q.clear(); + + assertTrue(q.isEmpty()); + } + + @Test + public void disposeWhileIteratorNext() { + final TestSubscriber ts = new TestSubscriber<>(10); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void disposeWhileIteratorNextConditional() { + final TestSubscriber ts = new TestSubscriber<>(10); + + Flowable.fromIterable(new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return true; + } + + @Override + public Integer next() { + ts.cancel(); + return 1; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }) + .filter(v -> true) + .subscribe(ts); + + ts.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromMaybeTest.java new file mode 100644 index 0000000000..d33b9f1e40 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromMaybeTest.java @@ -0,0 +1,93 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.TestSubscriberEx; + +public class FlowableFromMaybeTest extends RxJavaTest { + + @Test + public void success() { + Flowable.fromMaybe(Maybe.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Flowable.fromMaybe(Maybe.empty().hide()) + .test() + .assertResult(); + } + + @Test + public void error() { + Flowable.fromMaybe(Maybe.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelComposes() { + MaybeSubject ms = MaybeSubject.create(); + + TestSubscriber ts = Flowable.fromMaybe(ms) + .test(); + + ts.assertEmpty(); + + assertTrue(ms.hasObservers()); + + ts.cancel(); + + assertFalse(ms.hasObservers()); + } + + @Test + public void asyncFusion() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ASYNC); + + Flowable.fromMaybe(Maybe.just(1)) + .subscribe(ts); + + ts + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void syncFusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Flowable.fromMaybe(Maybe.just(1)) + .subscribe(ts); + + ts + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservableTest.java index 5689eadf49..f9f666d7fd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,4 +32,14 @@ public void error() { .test() .assertFailure(TestException.class); } + + @Test + public void all() { + for (BackpressureStrategy mode : BackpressureStrategy.values()) { + Flowable.fromObservable(Observable.range(1, 5), mode) + .test() + .withTag("mode: " + mode) + .assertResult(1, 2, 3, 4, 5); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromRunnableTest.java new file mode 100644 index 0000000000..19ef5eb018 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromRunnableTest.java @@ -0,0 +1,220 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Supplier; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class FlowableFromRunnableTest extends RxJavaTest { + @Test + public void fromRunnable() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Flowable.fromRunnable(new Runnable() { + @Override + public void run() { + atomicInteger.incrementAndGet(); + } + }) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromRunnableTwice() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Runnable run = new Runnable() { + @Override + public void run() { + atomicInteger.incrementAndGet(); + } + }; + + Flowable.fromRunnable(run) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + + Flowable.fromRunnable(run) + .test() + .assertResult(); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void fromRunnableInvokesLazy() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Flowable source = Flowable.fromRunnable(new Runnable() { + @Override + public void run() { + atomicInteger.incrementAndGet(); + } + }); + + assertEquals(0, atomicInteger.get()); + + source + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromRunnableThrows() { + Flowable.fromRunnable(new Runnable() { + @Override + public void run() { + throw new UnsupportedOperationException(); + } + }) + .test() + .assertFailure(UnsupportedOperationException.class); + } + + @SuppressWarnings("unchecked") + @Test + public void callable() throws Throwable { + final int[] counter = { 0 }; + + Flowable m = Flowable.fromRunnable(new Runnable() { + @Override + public void run() { + counter[0]++; + } + }); + + assertTrue(m.getClass().toString(), m instanceof Supplier); + + assertNull(((Supplier)m).get()); + + assertEquals(1, counter[0]); + } + + @Test + public void noErrorLoss() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + TestSubscriber ts = Flowable.fromRunnable(new Runnable() { + @Override + public void run() { + cdl1.countDown(); + try { + cdl2.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new TestException(e); + } + } + }).subscribeOn(Schedulers.single()).test(); + + assertTrue(cdl1.await(5, TimeUnit.SECONDS)); + + ts.cancel(); + + int timeout = 10; + + while (timeout-- > 0 && errors.isEmpty()) { + Thread.sleep(100); + } + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void disposedUpfront() throws Throwable { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run) + .test(1L, true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void cancelWhileRunning() { + final TestSubscriber ts = new TestSubscriber<>(); + + Flowable.fromRunnable(new Runnable() { + @Override + public void run() { + ts.cancel(); + } + }) + .subscribeWith(ts) + .assertEmpty(); + + assertTrue(ts.isCancelled()); + } + + @Test + public void asyncFused() throws Throwable { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ASYNC); + + Runnable action = mock(Runnable.class); + + Flowable.fromRunnable(action) + .subscribe(ts); + + ts.assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + + verify(action).run(); + } + + @Test + public void syncFusedRejected() throws Throwable { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Runnable action = mock(Runnable.class); + + Flowable.fromRunnable(action) + .subscribe(ts); + + ts.assertFusionMode(QueueFuseable.NONE) + .assertResult(); + + verify(action).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSingleTest.java new file mode 100644 index 0000000000..1aa1503299 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSingleTest.java @@ -0,0 +1,86 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.TestSubscriberEx; + +public class FlowableFromSingleTest extends RxJavaTest { + + @Test + public void success() { + Flowable.fromSingle(Single.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void error() { + Flowable.fromSingle(Single.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelComposes() { + SingleSubject ms = SingleSubject.create(); + + TestSubscriber ts = Flowable.fromSingle(ms) + .test(); + + ts.assertEmpty(); + + assertTrue(ms.hasObservers()); + + ts.cancel(); + + assertFalse(ms.hasObservers()); + } + + @Test + public void asyncFusion() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.ASYNC); + + Flowable.fromSingle(Single.just(1)) + .subscribe(ts); + + ts + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void syncFusionRejected() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + ts.setInitialFusionMode(QueueFuseable.SYNC); + + Flowable.fromSingle(Single.just(1)) + .subscribe(ts); + + ts + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSourceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSourceTest.java index 76fff6cb82..6506c010ea 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSourceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSourceTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public class FlowableFromSourceTest extends RxJavaTest { public void before() { source = new PublishAsyncEmitter(); sourceNoCancel = new PublishAsyncEmitterNoCancel(); - ts = new TestSubscriberEx(0L); + ts = new TestSubscriberEx<>(0L); } @Test @@ -137,7 +137,7 @@ public void normalError() { ts.assertError(MissingBackpressureException.class); ts.assertNotComplete(); - Assert.assertEquals("create: could not emit value due to lack of requests", ts.errors().get(0).getMessage()); + Assert.assertEquals("create: " + MissingBackpressureException.DEFAULT_MESSAGE, ts.errors().get(0).getMessage()); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplierTest.java index c44954bce3..423b1409c1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableFromSupplierTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; @@ -121,7 +118,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber outer = new TestSubscriber(subscriber); + TestSubscriber outer = new TestSubscriber<>(subscriber); fromSupplierFlowable .subscribeOn(Schedulers.computation()) @@ -248,7 +245,7 @@ public Object get() throws Exception { public void undeliverableUponCancellation() throws Exception { List errors = TestHelper.trackPluginErrors(); try { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.fromSupplier(new Supplier() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java index ecd965a4f8..9274685118 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGenerateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -282,4 +282,17 @@ public void accept(Emitter e) throws Exception { .test(1) .assertResult(); } + + @Test + public void onNextAfterOnComplete() { + Flowable.generate(new Consumer>() { + @Override + public void accept(Emitter e) throws Exception { + e.onComplete(); + e.onNext(1); + } + }) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java index 5dae57500f..d0d7dbea44 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupByTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,6 +18,7 @@ import static org.mockito.Mockito.*; import java.io.IOException; +import java.time.Duration; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; @@ -34,9 +35,9 @@ import io.reactivex.rxjava3.flowables.GroupedFlowable; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; @@ -45,6 +46,15 @@ public class FlowableGroupByTest extends RxJavaTest { + static Function, Flowable> FLATTEN_INTEGER = new Function, Flowable>() { + + @Override + public Flowable apply(GroupedFlowable t) { + return t; + } + + }; + final Function length = new Function() { @Override public Integer apply(String s) { @@ -102,6 +112,7 @@ public void empty() { } @Test + @SuppressUndeliverable public void error() { Flowable sourceStrings = Flowable.just("one", "two", "three", "four", "five", "six"); Flowable errorSource = Flowable.error(new TestException("forced failure")); @@ -111,7 +122,7 @@ public void error() { final AtomicInteger groupCounter = new AtomicInteger(); final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); grouped.flatMap(new Function, Flowable>() { @@ -156,13 +167,13 @@ public void onNext(String v) { private static Map> toMap(Flowable> flowable) { - final ConcurrentHashMap> result = new ConcurrentHashMap>(); + final ConcurrentHashMap> result = new ConcurrentHashMap<>(); flowable.doOnNext(new Consumer>() { @Override public void accept(final GroupedFlowable f) { - result.put(f.getKey(), new ConcurrentLinkedQueue()); + result.put(f.getKey(), new ConcurrentLinkedQueue<>()); f.subscribe(new Consumer() { @Override @@ -605,7 +616,7 @@ public void accept(String s) { @Test public void firstGroupsCompleteAndParentSlowToThenEmitFinalGroupsAndThenComplete() throws InterruptedException { final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Flowable.unsafeCreate(new Publisher() { @Override @@ -684,7 +695,7 @@ public void accept(String s) { public void firstGroupsCompleteAndParentSlowToThenEmitFinalGroupsWhichThenSubscribesOnAndDelaysAndThenCompletes() throws InterruptedException { System.err.println("----------------------------------------------------------------------------------------------"); final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Flowable.unsafeCreate(new Publisher() { @Override @@ -776,7 +787,7 @@ public void accept(String s) { @Test public void firstGroupsCompleteAndParentSlowToThenEmitFinalGroupsWhichThenObservesOnAndDelaysAndThenCompletes() throws InterruptedException { final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Flowable.unsafeCreate(new Publisher() { @Override @@ -853,7 +864,7 @@ public void accept(String s) { @Test public void groupsWithNestedSubscribeOn() throws InterruptedException { - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Flowable.unsafeCreate(new Publisher() { @Override @@ -910,7 +921,7 @@ public void accept(String s) { @Test public void groupsWithNestedObserveOn() throws InterruptedException { - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Flowable.unsafeCreate(new Publisher() { @Override @@ -1035,7 +1046,7 @@ public Boolean apply(Integer n) { @Test public void groupByBackpressure() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 4000) .groupBy(IS_EVEN2) @@ -1162,7 +1173,7 @@ public String apply(String v) { } }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); m.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); System.out.println("ts .get " + ts.values()); @@ -1178,7 +1189,7 @@ public void keySelectorThrows() { Flowable m = source.groupBy(fail(0), dbl).flatMap(FLATTEN_INTEGER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); m.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); assertEquals(1, ts.errors().size()); @@ -1186,11 +1197,12 @@ public void keySelectorThrows() { } @Test + @SuppressUndeliverable public void valueSelectorThrows() { Flowable source = Flowable.just(0, 1, 2, 3, 4, 5, 6); Flowable m = source.groupBy(identity, fail(0)).flatMap(FLATTEN_INTEGER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); m.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); assertEquals(1, ts.errors().size()); @@ -1204,7 +1216,7 @@ public void innerEscapeCompleted() { Flowable m = source.groupBy(identity, dbl).flatMap(FLATTEN_INTEGER); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); m.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertNoErrors(); @@ -1218,7 +1230,7 @@ public void innerEscapeCompleted() { public void exceptionIfSubscribeToChildMoreThanOnce() { Flowable source = Flowable.just(0); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); Flowable> m = source.groupBy(identity, dbl); @@ -1241,13 +1253,14 @@ public void accept(GroupedFlowable t1) { } @Test + @SuppressUndeliverable public void error2() { Flowable source = Flowable.concat(Flowable.just(0), Flowable. error(new TestException("Forced failure"))); Flowable m = source.groupBy(identity, dbl).flatMap(FLATTEN_INTEGER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); m.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); assertEquals(1, ts.errors().size()); @@ -1256,7 +1269,7 @@ public void error2() { @Test public void groupByBackpressure3() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 4000).groupBy(IS_EVEN2).flatMap(new Function, Flowable>() { @@ -1313,7 +1326,7 @@ public void accept(Notification t1) { @Test public void groupByBackpressure2() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 4000) .doOnNext(new Consumer() { @@ -1350,19 +1363,10 @@ public String apply(Integer l) { ts.assertNoErrors(); } - static Function, Flowable> FLATTEN_INTEGER = new Function, Flowable>() { - - @Override - public Flowable apply(GroupedFlowable t) { - return t; - } - - }; - @Test public void groupByWithNullKey() { final String[] key = new String[]{"uninitialized"}; - final List values = new ArrayList(); + final List values = new ArrayList<>(); Flowable.just("a", "b", "c").groupBy(new Function() { @Override @@ -1398,7 +1402,7 @@ public void subscribe(Subscriber subscriber) { } } ); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); f.groupBy(new Function() { @@ -1416,11 +1420,11 @@ public Integer apply(Integer integer) { @Test public void groupByShouldPropagateError() { final Throwable e = new RuntimeException("Oops"); - final TestSubscriberEx inner1 = new TestSubscriberEx(); - final TestSubscriberEx inner2 = new TestSubscriberEx(); + final TestSubscriberEx inner1 = new TestSubscriberEx<>(); + final TestSubscriberEx inner2 = new TestSubscriberEx<>(); final TestSubscriberEx> outer - = new TestSubscriberEx>(new DefaultSubscriber>() { + = new TestSubscriberEx<>(new DefaultSubscriber>() { @Override public void onComplete() { @@ -1557,7 +1561,7 @@ public Object apply(Integer i) { */ @Test public void backpressureInnerDoesntOverflowOuter() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); PublishProcessor pp = PublishProcessor.create(); @@ -1586,7 +1590,7 @@ public void accept(GroupedFlowable g) { @Test public void backpressureInnerDoesntOverflowOuterMissingBackpressure() { - TestSubscriber> ts = new TestSubscriber>(1); + TestSubscriber> ts = new TestSubscriber<>(1); Flowable.fromArray(1, 2) .groupBy(new Function() { @@ -1611,9 +1615,9 @@ public void accept(GroupedFlowable g) { @Test public void oneGroupInnerRequestsTwiceBuffer() { // FIXME: delayed requesting in groupBy results in group abandonment - TestSubscriber ts1 = new TestSubscriber(1L); + TestSubscriber ts1 = new TestSubscriber<>(1L); - final TestSubscriber ts2 = new TestSubscriber(0L); + final TestSubscriber ts2 = new TestSubscriber<>(0L); Flowable.range(1, Flowable.bufferSize() * 2) .groupBy(new Function() { @@ -1671,7 +1675,9 @@ public void accept(GroupedFlowable g) { .subscribe(ts2); ts1 - .assertFusionMode(QueueFuseable.ASYNC) + // FIXME fusion mode causes hangs + //.assertFusionMode(QueueFuseable.ASYNC) + .assertFusionMode(QueueFuseable.NONE) .assertValues(2, 3, 4, 5, 6, 7, 8, 9, 10, 11) .assertNoErrors() .assertComplete(); @@ -1683,6 +1689,7 @@ public void accept(GroupedFlowable g) { } @Test + @SuppressUndeliverable public void keySelectorAndDelayError() { Flowable.just(1).concatWith(Flowable.error(new TestException())) .groupBy(Functions.identity(), true) @@ -1697,6 +1704,7 @@ public Flowable apply(GroupedFlowable g) throws Excep } @Test + @SuppressUndeliverable public void keyAndValueSelectorAndDelayError() { Flowable.just(1).concatWith(Flowable.error(new TestException())) .groupBy(Functions.identity(), Functions.identity(), true) @@ -1805,10 +1813,21 @@ public Object apply(Flowable f) throws Exception { @Test public void badRequest() { - TestHelper.assertBadRequestReported(Flowable.just(1) + TestHelper.assertBadRequestReported(Flowable.just(1).hide() .groupBy(Functions.justFunction(1))); } + @Test + public void badRequestInner() { + Flowable.just(1).hide() + .groupBy(Functions.justFunction(1)) + .doOnNext(g -> { + TestHelper.assertBadRequestReported(g); + }) + .test() + .assertNoErrors(); + } + @Test public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowable(new Function, Publisher>>() { @@ -1839,6 +1858,7 @@ public Publisher apply(GroupedFlowable g) throws Excep } @Test + @SuppressUndeliverable public void groupError() { Flowable.just(1).concatWith(Flowable.error(new TestException())) .groupBy(Functions.justFunction(1), true) @@ -1883,10 +1903,43 @@ public Map apply(final Consumer notify) throws Exceptio .assertNoValues() .assertError(ex); } + // ----------------------------------------------------------------------------------------------------------------------- + + private static final Function mod5 = new Function() { + + @Override + public Integer apply(Integer n) throws Exception { + return n % 5; + } + }; + + private static Function, Publisher> addCompletedKey( + final List completed) { + return new Function, Publisher>() { + @Override + public Publisher apply(final GroupedFlowable g) throws Exception { + return g.doOnComplete(new Action() { + @Override + public void run() throws Exception { + completed.add(g.getKey()); + } + }); + } + }; + } + + private static final class TestTicker extends Ticker { + long tick; + + @Override + public long read() { + return tick; + } + } @Test public void mapFactoryExpiryCompletesGroupedFlowable() { - final List completed = new CopyOnWriteArrayList(); + final List completed = new CopyOnWriteArrayList<>(); Function, Map> evictingMapFactory = createEvictingMapFactorySynchronousOnly(1); PublishSubject subject = PublishSubject.create(); TestSubscriberEx ts = subject.toFlowable(BackpressureStrategy.BUFFER) @@ -1905,32 +1958,6 @@ public void mapFactoryExpiryCompletesGroupedFlowable() { ts.assertValueCount(3); } - private static final Function mod5 = new Function() { - - @Override - public Integer apply(Integer n) throws Exception { - return n % 5; - } - }; - - @Test - public void mapFactoryWithExpiringGuavaCacheDemonstrationCodeForUseInJavadoc() { - //javadoc will be a version of this using lambdas and without assertions - final List completed = new CopyOnWriteArrayList(); - //size should be less than 5 to notice the effect - Function, Map> evictingMapFactory = createEvictingMapFactoryGuava(3); - int numValues = 1000; - TestSubscriber ts = - Flowable.range(1, numValues) - .groupBy(mod5, Functions.identity(), true, 16, evictingMapFactory) - .flatMap(addCompletedKey(completed)) - .test() - .assertComplete(); - ts.assertValueCount(numValues); - //the exact eviction behaviour of the guava cache is not specified so we make some approximate tests - assertTrue(completed.size() > numValues * 0.9); - } - @Test public void mapFactoryEvictionQueueClearedOnErrorCoverageOnly() { Function, Map> evictingMapFactory = createEvictingMapFactorySynchronousOnly(1); @@ -1952,28 +1979,28 @@ public Publisher apply(GroupedFlowable g) throws Exce .assertError(ex); } - private static Function, Publisher> addCompletedKey( - final List completed) { - return new Function, Publisher>() { - @Override - public Publisher apply(final GroupedFlowable g) throws Exception { - return g.doOnComplete(new Action() { - @Override - public void run() throws Exception { - completed.add(g.getKey()); - } - }); - } - }; - } + @Test + public void mapFactoryWithExpiringGuavaCacheDemonstrationCodeForUseInJavadoc() { + //javadoc will be a version of this using lambdas and without assertions + final List completed = new CopyOnWriteArrayList<>(); - private static final class TestTicker extends Ticker { - long tick; + AtomicReference> cacheOut = new AtomicReference<>(); - @Override - public long read() { - return tick; - } + //size should be less than 5 to notice the effect + Function, Map> evictingMapFactory = createEvictingMapFactoryGuava(3, cacheOut); + int numValues = 1000; + TestSubscriber ts = + Flowable.range(1, numValues) + .groupBy(mod5, Functions.identity(), true, 16, evictingMapFactory) + .flatMap(addCompletedKey(completed)) + .test() + .assertComplete() + ; + ts.assertValueCount(numValues); + //the exact eviction behaviour of the guava cache is not specified so we make some approximate tests + assertTrue(completed.size() > numValues * 0.9); + + cacheOut.get().invalidateAll(); } @Test @@ -1999,7 +2026,7 @@ public void onRemoval(RemovalNotification notification) { } }; - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); Flowable stream = source // .doOnCancel(new Action() { @Override @@ -2066,33 +2093,11 @@ public void run() throws Exception { ), list); } - @Test - public void cancellationOfUpstreamWhenGroupedFlowableCompletes() { - final AtomicBoolean cancelled = new AtomicBoolean(); - Flowable.just(1).repeat().doOnCancel(new Action() { - @Override - public void run() throws Exception { - cancelled.set(true); - } - }) - .groupBy(Functions.identity(), Functions.identity()) // - .flatMap(new Function, Publisher>() { - @Override - public Publisher apply(GroupedFlowable g) throws Exception { - return g.first(0).toFlowable(); - } - }) - .take(4) // - .test() // - .assertComplete(); - assertTrue(cancelled.get()); - } - //not thread safe private static final class SingleThreadEvictingHashMap implements Map { - private final List list = new ArrayList(); - private final Map map = new HashMap(); + private final List list = new ArrayList<>(); + private final Map map = new HashMap<>(); private final int maxSize; private final Consumer evictedListener; @@ -2185,13 +2190,14 @@ public Set> entrySet() { } } - private static Function, Map> createEvictingMapFactoryGuava(final int maxSize) { + private static Function, Map> createEvictingMapFactoryGuava(final int maxSize, + final AtomicReference> cacheOut) { Function, Map> evictingMapFactory = // new Function, Map>() { @Override public Map apply(final Consumer notify) throws Exception { - return CacheBuilder.newBuilder() // + Cache cache = CacheBuilder.newBuilder() // .maximumSize(maxSize) // .removalListener(new RemovalListener() { @Override @@ -2202,8 +2208,9 @@ public void onRemoval(RemovalNotification notification) { throw new RuntimeException(e); } }}) - . build() - .asMap(); + . build(); + cacheOut.set(cache); + return cache.asMap(); }}; return evictingMapFactory; } @@ -2214,24 +2221,49 @@ private static Function, Map> createEvictingMa @Override public Map apply(final Consumer notify) throws Exception { - return new SingleThreadEvictingHashMap(maxSize, new Consumer() { - @Override - public void accept(Object object) { - try { - notify.accept(object); - } catch (Throwable e) { - throw new RuntimeException(e); - } - }}); + return new SingleThreadEvictingHashMap<>(maxSize, new Consumer() { + @Override + public void accept(Object object) { + try { + notify.accept(object); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + }); }}; return evictingMapFactory; } + // ----------------------------------------------------------------------------------------------------------------------- + + @Test + public void cancellationOfUpstreamWhenGroupedFlowableCompletes() { + final AtomicBoolean cancelled = new AtomicBoolean(); + Flowable.just(1).repeat().doOnCancel(new Action() { + @Override + public void run() throws Exception { + cancelled.set(true); + } + }) + .groupBy(Functions.identity(), Functions.identity()) // + .flatMap(new Function, Publisher>() { + @Override + public Publisher apply(GroupedFlowable g) throws Exception { + return g.first(0).toFlowable(); + } + }) + .take(4) // + .test() // + .assertComplete(); + assertTrue(cancelled.get()); + } + @Test public void cancelOverFlatmapRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final PublishProcessor pp = PublishProcessor.create(); @@ -2274,7 +2306,7 @@ public void run() { @Test public void abandonedGroupsNoDataloss() { - final List> groups = new ArrayList>(); + final List> groups = new ArrayList<>(); Flowable.range(1, 1000) .groupBy(new Function() { @@ -2303,8 +2335,8 @@ public void accept(GroupedFlowable v) throws Throwable { @Test public void newGroupValueSelectorFails() { - TestSubscriber ts1 = new TestSubscriber(); - final TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); + final TestSubscriber ts2 = new TestSubscriber<>(); Flowable.just(1) .groupBy(Functions.identity(), new Function() { @@ -2330,8 +2362,8 @@ public void accept(GroupedFlowable g) throws Throwable { @Test public void existingGroupValueSelectorFails() { - TestSubscriber ts1 = new TestSubscriber(); - final TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); + final TestSubscriber ts2 = new TestSubscriber<>(); Flowable.just(1, 2) .groupBy(Functions.justFunction(1), new Function() { @@ -2389,7 +2421,6 @@ public Publisher apply(GroupedFlowable g) { } @Test - @SuppressWarnings("unchecked") public void valueSelectorCrashAndMissingBackpressure() { PublishProcessor pp = PublishProcessor.create(); @@ -2399,7 +2430,7 @@ public Integer apply(Integer t) throws Throwable { throw new TestException(); } }) - .subscribeWith(new TestSubscriberEx>(0L)); + .subscribeWith(new TestSubscriberEx<>(0L)); assertTrue(pp.offer(1)); @@ -2444,4 +2475,484 @@ public void accept(Integer v) throws Throwable { .assertNoErrors() .assertComplete(); } + + @Test + public void cancelledGroupResumesRequesting() { + final List> tss = new ArrayList<>(); + final AtomicInteger counter = new AtomicInteger(); + final AtomicBoolean done = new AtomicBoolean(); + Flowable.range(1, 1000) + .doOnNext(new Consumer() { + @Override + public void accept(Integer v) throws Exception { + counter.getAndIncrement(); + } + }) + .groupBy(Functions.justFunction(1)) + .subscribe(new Consumer>() { + @Override + public void accept(GroupedFlowable v) throws Exception { + TestSubscriber ts = TestSubscriber.create(0L); + tss.add(ts); + v.subscribe(ts); + } + }, Functions.emptyConsumer(), new Action() { + @Override + public void run() throws Exception { + done.set(true); + } + }); + + while (!done.get()) { + tss.remove(0).cancel(); + } + + assertEquals(1000, counter.get()); + } + + @Test + public void delayErrorCompleteMoreWorkInGroup() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.groupBy(v -> 1, true) + .flatMap(g -> g.doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onComplete(); + } + }) + ) + .test() + ; + + pp.onNext(1); + + ts.assertResult(1, 2); + } + + @Test + public void groupSyncFusionRejected() { + Flowable.just(1) + .groupBy(v -> 1) + .doOnNext(g -> { + g.subscribeWith(new TestSubscriberEx().setInitialFusionMode(QueueFuseable.SYNC)) + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + }) + .test() + .assertComplete(); + } + + @Test + public void subscribeAbandonRace() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = TestSubscriber.create(); + + CountDownLatch cdl = new CountDownLatch(1); + + pp.groupBy(v -> 1) + .doOnNext(g -> { + TestHelper.raceOther(() -> { + g.subscribe(ts); + }, cdl); + }) + .test(); + + pp.onNext(1); + + cdl.await(); + + ts.assertValueCount(1); + } + } + + @Test + public void issue6974() { + + FlowableTransformer operation = + source -> source.publish(shared -> + shared + .firstElement() + .flatMapPublisher(firstElement -> + Flowable.just(firstElement).concatWith(shared) + ) + ); + + issue6974Run(20, 500_000, 20 - 1, 20 * 2, operation, false); + + issue6974Run(20, 500_000, 20, 20 * 2, operation, false); + } + + static void issue6974Run(int groups, int iterations, int sizeCap, int flatMapConcurrency, + FlowableTransformer operation, boolean notifyOnExplicitRevoke) { + TestSubscriber test = Flowable + .range(1, groups) + .repeat(iterations / groups) + .groupBy(i -> i, i -> i, false, 128, sizeCap(sizeCap, notifyOnExplicitRevoke)) + .flatMap(gf -> gf.compose(operation), flatMapConcurrency) + .test(); + test.awaitDone(5, TimeUnit.SECONDS); + test.assertValueCount(iterations); + } + + static Function, Map> sizeCap(int maxCapacity, boolean notifyOnExplicit) { + return itemEvictConsumer -> + CacheBuilder + .newBuilder() + .maximumSize(maxCapacity) + .removalListener(notification -> { + if (notification.getCause() != RemovalCause.EXPLICIT || notifyOnExplicit) { + try { + itemEvictConsumer.accept(notification.getValue()); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + }) + .build().asMap(); + } + + static void issue6974RunPart2(int groupByBufferSize, int flatMapMaxConcurrency, int groups, + boolean notifyOnExplicitEviction) { + TestSubscriber ts = Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + // set cap too high + sizeCap(groups * 100, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test(); + + ts + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + issue6974RunPart2(groupByBufferSize, flatMapMaxConcurrency, groups, notifyOnExplicitEviction); + } + + @Test + public void issue6974Part2Case2() { + final int groups = 20; + + // Timeout... explicit eviction notification makes difference + int groupByBufferSize = groups * 30; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = true; + issue6974RunPart2(groupByBufferSize, flatMapMaxConcurrency, groups, notifyOnExplicitEviction); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case2Loop() { + for (int i = 0; i < 1000; i++) { + issue6974Part2Case2(); + } + } + */ + + static void issue6974RunPart2NoEvict(int groupByBufferSize, int flatMapMaxConcurrency, int groups, + boolean notifyOnExplicitEviction) { + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i) + .flatMap(gf -> gf + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .subscribeWith(new TestSubscriberEx<>()) + .awaitDone(5, TimeUnit.SECONDS) + .assertTerminated(); // MBE is possible if the async group closing is slow + } + + @Test + public void issue6974Part2Case1NoEvict() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + issue6974RunPart2NoEvict(groupByBufferSize, flatMapMaxConcurrency, groups, notifyOnExplicitEviction); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case1NoEvictLoop() { + for (int i = 0; i < 1000; i++) { + issue6974Part2Case1NoEvict(); + } + } + */ + + @Test + public void issue6974Part2Case1ObserveOn() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> { + System.out.println("Cancelling upstream"); + }) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .subscribeWith(new TestSubscriberEx<>()) + .awaitDone(5, TimeUnit.SECONDS) + .assertTerminated(); // MBE is possible if the async group closing is slow + } + + @Test + public void issue6974Part2Case1ObserveOnHide() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> System.out.println("Cancelling upstream")) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .hide() + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .subscribeWith(new TestSubscriberEx<>()) + .awaitDone(5, TimeUnit.SECONDS) + .assertTerminated(); // MBE is possible if the async group closing is slow + } + + @Test + public void issue6974Part2Case1ObserveOnNoCap() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int flatMapMaxConcurrency = 1_000_000; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnRequest(v -> { + System.out.println("Source: " + v); + }) + .groupBy(i -> i) + .flatMap(gf -> gf + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void issue6974Part2Case1ObserveOnNoCapHide() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int flatMapMaxConcurrency = 1_000_000; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnRequest(v -> { + System.out.println("Source: " + v); + }) + .groupBy(i -> i) + .flatMap(gf -> gf + .hide() + .observeOn(Schedulers.computation()) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case1ObserveOnNoCapHideLoop() { + for (int i = 0; i < 100; i++) { + issue6974Part2Case1ObserveOnNoCapHide(); + } + } + */ + + @Test + public void issue6974Part2Case1ObserveOnConditional() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> System.out.println("Cancelling upstream")) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .observeOn(Schedulers.computation()) + .filter(v -> true) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .subscribeWith(new TestSubscriberEx<>()) + .awaitDone(5, TimeUnit.SECONDS) + .assertTerminated(); // MBE is possible if the async group closing is slow + } + + @Test + public void issue6974Part2Case1ObserveOnConditionalHide() { + final int groups = 20; + + // Not completed (Timed out), buffer is too small + int groupByBufferSize = groups * 2; + int flatMapMaxConcurrency = 2 * groups; + boolean notifyOnExplicitEviction = false; + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .doOnCancel(() -> System.out.println("Cancelling upstream")) + .groupBy(i -> i, i -> i, false, groupByBufferSize, + sizeCap(groups * 2, notifyOnExplicitEviction)) + .flatMap(gf -> gf + .hide() + .observeOn(Schedulers.computation()) + .filter(v -> true) + // .take(10) + .take(10, TimeUnit.MILLISECONDS) + , flatMapMaxConcurrency) + .subscribeWith(new TestSubscriberEx<>()) + .awaitDone(5, TimeUnit.SECONDS) + .assertTerminated(); // MBE is possible if the async group closing is slow + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6974Part2Case1ObserveOnHideLoop() { + for (int i = 0; i < 100; i++) { + issue6974Part2Case1ObserveOnHide(); + } + } + */ + + static Function, ConcurrentMap> ttlCapGuava(Duration ttl) { + return itemEvictConsumer -> + CacheBuilder + .newBuilder() + .expireAfterWrite(ttl) + .removalListener(n -> { + if (n.getCause() != com.google.common.cache.RemovalCause.EXPLICIT) { + try { + itemEvictConsumer.accept(n.getValue()); + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + }).build().asMap(); + } + + @Test + public void issue6982Case1() { + final int groups = 20; + + int groupByBufferSize = 2; + int flatMapMaxConcurrency = 200 * groups; + + // ~50% of executions - Not completed (latch = 1, values = 500000, errors = 0, completions = 0, timeout!, + // disposed!) + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i, i -> i, false, groupByBufferSize, ttlCapGuava(Duration.ofMillis(10))) + .flatMap(gf -> gf.observeOn(Schedulers.computation()), flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6982Case1Loop() { + for (int i = 0; i < 200; i++) { + System.out.println("issue6982Case1Loop " + i); + issue6982Case1(); + } + } + */ + + @Test + public void issue6982Case2() { + final int groups = 20; + + int groupByBufferSize = groups * 30; + int flatMapMaxConcurrency = groups * 500; + // Always : Not completed (latch = 1, values = 14100, errors = 0, completions = 0, timeout!, disposed!) + + Flowable + .range(1, 500_000) + .map(i -> i % groups) + .groupBy(i -> i, i -> i, false, groupByBufferSize, ttlCapGuava(Duration.ofMillis(10))) + .flatMap(gf -> gf.observeOn(Schedulers.computation()), flatMapMaxConcurrency) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertNoErrors() + .assertComplete(); + } + + /* + * Disabled: Takes very long. Run it locally only. + @Test + public void issue6982Case2Loop() { + for (int i = 0; i < 200; i++) { + System.out.println("issue6982Case2Loop " + i); + issue6982Case2(); + } + } + */ } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java index bed0686923..fe83ad9fff 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableGroupJoinTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.flowable; import static org.junit.Assert.*; @@ -31,7 +29,7 @@ import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.operators.flowable.FlowableGroupJoin.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -79,7 +77,7 @@ public Integer apply(Integer rightValue) throws Throwable { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test @@ -732,4 +730,55 @@ public void leftRightEndState() { verify(js).innerClose(false, o); } + + @Test + public void disposeAfterOnNext() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + pp1.groupJoin(pp2, v -> Flowable.never(), v -> Flowable.never(), (a, b) -> a) + .doOnNext(v -> { + ts.cancel(); + }) + .subscribe(ts); + + pp2.onNext(1); + pp1.onNext(1); + } + + @Test + public void completeWithMoreWork() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + pp1.groupJoin(pp2, v -> Flowable.never(), v -> Flowable.never(), (a, b) -> a) + .doOnNext(v -> { + if (v == 1) { + pp2.onNext(2); + pp1.onComplete(); + pp2.onComplete(); + } + }) + .subscribe(ts); + + pp2.onNext(1); + pp1.onNext(1); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().groupJoin(Flowable.never(), v -> Flowable.never(), v -> Flowable.never(), (a, b) -> a)); + } + + @Test + public void missingBackpressure() { + Flowable.just(1) + .groupJoin(Flowable.never(), v -> BehaviorProcessor.createDefault(1), v -> Flowable.never(), (a, b) -> a) + .test(0) + .assertFailure(MissingBackpressureException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHideTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHideTest.java index c2162b2872..f95775ebda 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHideTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableHideTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsTest.java index e88319a1dd..dfc0e11680 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIgnoreElementsTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,8 +23,9 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.observers.DisposableCompletableObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.*; @@ -61,7 +62,7 @@ public void accept(Integer t) { @Test public void completedOkFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(1, 10).ignoreElements().toFlowable().subscribe(ts); ts.assertNoErrors(); ts.assertNoValues(); @@ -70,7 +71,7 @@ public void completedOkFlowable() { @Test public void errorReceivedFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); TestException ex = new TestException("boo"); Flowable.error(ex).ignoreElements().toFlowable().subscribe(ts); ts.assertNoValues(); @@ -173,7 +174,7 @@ public void accept(Integer t) { @Test public void completedOk() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Flowable.range(1, 10).ignoreElements().subscribe(to); to.assertNoErrors(); to.assertNoValues(); @@ -182,7 +183,7 @@ public void completedOk() { @Test public void errorReceived() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); TestException ex = new TestException("boo"); Flowable.error(ex).ignoreElements().subscribe(to); to.assertNoValues(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelperTest.java index e570842c98..1549f01d63 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.flowable; import org.junit.Test; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java index 46cf13229b..f8bc3355e9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalRangeTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; @@ -118,4 +115,12 @@ public void cancel() { .test() .assertResult(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L); } + + @Test + public void takeSameAsRange() { + Flowable.intervalRange(0, 2, 1, 1, TimeUnit.MILLISECONDS, Schedulers.trampoline()) + .take(2) + .test() + .assertResult(0L, 1L); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalTest.java index a5ec88c154..7fde08d47f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableIntervalTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public void badRequest() { @Test public void cancelledOnRun() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); IntervalSubscriber is = new IntervalSubscriber(ts); ts.onSubscribe(is); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java index 7c33df579c..fe28a93320 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableJoinTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.flowable; import static org.mockito.ArgumentMatchers.any; @@ -55,7 +53,7 @@ public Flowable apply(Integer t1) { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test @@ -487,4 +485,35 @@ public Object apply(Integer a, Integer b) throws Exception { ts.assertFailure(MissingBackpressureException.class); } + + @Test + public void badRequest() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestHelper.assertBadRequestReported(pp1.join(pp2, Functions.justFunction(Flowable.never()), Functions.justFunction(Flowable.never()), (a, b) -> a + b)); + } + + @Test + public void bothTerminateWithWorkRemaining() { + PublishProcessor pp1 = PublishProcessor.create(); + PublishProcessor pp2 = PublishProcessor.create(); + + TestSubscriber ts = pp1.join( + pp2, + v -> Flowable.never(), + v -> Flowable.never(), + (a, b) -> a + b) + .doOnNext(v -> { + pp1.onComplete(); + pp2.onNext(2); + pp2.onComplete(); + }) + .test(); + + pp1.onNext(0); + pp2.onNext(1); + + ts.assertComplete(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastTest.java index 1897d236d6..3164802ab6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLiftTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLiftTest.java index c208b8370b..68227e8dfc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLiftTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableLiftTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotificationTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotificationTest.java index 3ec6b8f771..905cda5548 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotificationTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapNotificationTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public class FlowableMapNotificationTest extends RxJavaTest { @Test public void just() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1) .flatMap( new Function>() { @@ -61,7 +61,7 @@ public Flowable get() { public void backpressure() { TestSubscriber ts = TestSubscriber.create(0L); - new FlowableMapNotification(Flowable.range(1, 3), + new FlowableMapNotification<>(Flowable.range(1, 3), new Function() { @Override public Integer apply(Integer item) { @@ -105,7 +105,7 @@ public void noBackpressure() { PublishProcessor pp = PublishProcessor.create(); - new FlowableMapNotification(pp, + new FlowableMapNotification<>(pp, new Function() { @Override public Integer apply(Integer item) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java index 35dba45e2b..3d77fe5d46 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,8 +27,10 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -155,7 +157,7 @@ public String apply(Map map) { @Test public void mapWithError() { - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); Flowable w = Flowable.just("one", "fail", "two", "three", "fail"); Flowable m = w.map(new Function() { @@ -261,7 +263,7 @@ public Integer apply(Integer i) { } private static Map getMap(String prefix) { - Map m = new HashMap(); + Map m = new HashMap<>(); m.put("firstName", prefix + "First"); m.put("lastName", prefix + "Last"); return m; @@ -272,7 +274,7 @@ public void functionCrashUnsubscribes() { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); pp.map(new Function() { @Override @@ -583,13 +585,13 @@ public void fusedSync() { public void fusedAsync() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - us + up .map(Functions.identity()) .subscribe(ts); - TestHelper.emit(us, 1, 2, 3, 4, 5); + TestHelper.emit(up, 1, 2, 3, 4, 5); ts.assertFusionMode(QueueFuseable.ASYNC) .assertResult(1, 2, 3, 4, 5); @@ -617,4 +619,19 @@ public Object apply(Flowable f) throws Exception { }, false, 1, 1, 1); } + @Test + public void conditionalFusionNoNPE() { + TestSubscriberEx ts = new TestSubscriberEx<>() + .setInitialFusionMode(QueueFuseable.ANY); + + Flowable.empty() + .observeOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + .map(v -> v) + .filter(v -> true) + .subscribe(ts) + ; + + ts.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterializeTest.java index a6f63f3cf2..0f9c3df225 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMaterializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -102,7 +102,7 @@ public void multipleSubscribes() throws InterruptedException, ExecutionException @Test public void backpressureOnEmptyStream() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); Flowable. empty().materialize().subscribe(ts); ts.assertNoValues(); ts.request(1); @@ -113,7 +113,7 @@ public void backpressureOnEmptyStream() { @Test public void backpressureNoError() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); Flowable.just(1, 2, 3).materialize().subscribe(ts); ts.assertNoValues(); ts.request(1); @@ -127,7 +127,7 @@ public void backpressureNoError() { @Test public void backpressureNoErrorAsync() throws InterruptedException { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); Flowable.just(1, 2, 3) .materialize() .subscribeOn(Schedulers.computation()) @@ -148,7 +148,7 @@ public void backpressureNoErrorAsync() throws InterruptedException { @Test public void backpressureWithError() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); Flowable. error(new IllegalArgumentException()).materialize().subscribe(ts); ts.assertNoValues(); ts.request(1); @@ -158,7 +158,7 @@ public void backpressureWithError() { @Test public void backpressureWithEmissionThenError() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); IllegalArgumentException ex = new IllegalArgumentException(); Flowable.fromIterable(Arrays.asList(1)).concatWith(Flowable. error(ex)).materialize() .subscribe(ts); @@ -175,7 +175,7 @@ public void backpressureWithEmissionThenError() { @Test public void withCompletionCausingError() { - TestSubscriberEx> ts = new TestSubscriberEx>(); + TestSubscriberEx> ts = new TestSubscriberEx<>(); final RuntimeException ex = new RuntimeException("boo"); Flowable.empty().materialize().doOnNext(new Consumer() { @Override @@ -190,7 +190,7 @@ public void accept(Object t) { @Test public void unsubscribeJustBeforeCompletionNotificationShouldPreventThatNotificationArriving() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); Flowable.empty().materialize() .subscribe(ts); @@ -204,7 +204,7 @@ private static class TestNotificationSubscriber extends DefaultSubscriber> notifications = new Vector>(); + List> notifications = new Vector<>(); @Override public void onComplete() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeDelayErrorTest.java index f80323abdd..1959921168 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeDelayErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeDelayErrorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -257,7 +257,7 @@ public void mergeArray() { public void mergeList() { final Flowable f1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable f2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); - List> listOfFlowables = new ArrayList>(); + List> listOfFlowables = new ArrayList<>(); listOfFlowables.add(f1); listOfFlowables.add(f2); @@ -438,7 +438,7 @@ public void onNext(String args) { @Test public void errorInParentFlowable() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.mergeDelayError( Flowable.just(Flowable.just(1), Flowable.just(2)) .startWithItem(Flowable. error(new RuntimeException())) @@ -467,7 +467,7 @@ public void subscribe(Subscriber> op) { stringSubscriber = TestHelper.mockSubscriber(); - TestSubscriberEx ts = new TestSubscriberEx(stringSubscriber); + TestSubscriberEx ts = new TestSubscriberEx<>(stringSubscriber); Flowable m = Flowable.mergeDelayError(parentFlowable); m.subscribe(ts); System.out.println("testErrorInParentFlowableDelayed | " + i); @@ -506,7 +506,7 @@ public void run() { @Test public void delayErrorMaxConcurrent() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable source = Flowable.mergeDelayError(Flowable.just( Flowable.just(1).hide(), Flowable.error(new TestException())) @@ -517,7 +517,7 @@ public void accept(long t1) { } }), 1); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.subscribe(ts); @@ -532,7 +532,7 @@ public void accept(long t1) { public void mergeIterable() { final Flowable f1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable f2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); - List> listOfFlowables = new ArrayList>(); + List> listOfFlowables = new ArrayList<>(); listOfFlowables.add(f1); listOfFlowables.add(f2); @@ -544,7 +544,6 @@ public void mergeIterable() { verify(stringSubscriber, times(2)).onNext("hello"); } - @SuppressWarnings("unchecked") @Test public void iterableMaxConcurrent() { TestSubscriber ts = TestSubscriber.create(); @@ -571,10 +570,9 @@ public void iterableMaxConcurrent() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void iterableMaxConcurrentError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); PublishProcessor pp1 = PublishProcessor.create(); PublishProcessor pp2 = PublishProcessor.create(); @@ -624,7 +622,6 @@ public void array() { } } - @SuppressWarnings("unchecked") @Test public void mergeArrayDelayError() { Flowable.mergeArrayDelayError(Flowable.just(1), Flowable.just(2)) @@ -632,7 +629,6 @@ public void mergeArrayDelayError() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorWithError() { Flowable.mergeDelayError( @@ -678,7 +674,6 @@ public void mergeDelayErrorWithErrorMaxConcurrency() { .assertFailure(TestException.class, 1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorMaxConcurrency() { Flowable.mergeDelayError( @@ -688,7 +683,6 @@ public void mergeIterableDelayErrorMaxConcurrency() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorWithErrorMaxConcurrency() { Flowable.mergeDelayError( @@ -720,7 +714,6 @@ public void mergeDelayError3WithError() { .assertFailure(TestException.class, 1, 2, 3); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayError() { Flowable.mergeDelayError(Arrays.asList(Flowable.just(1), Flowable.just(2))) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeMaxConcurrentTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeMaxConcurrentTest.java index 979c29a9aa..25b9cd825a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeMaxConcurrentTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeMaxConcurrentTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,14 +34,14 @@ public class FlowableMergeMaxConcurrentTest extends RxJavaTest { @Test public void whenMaxConcurrentIsOne() { for (int i = 0; i < 100; i++) { - List> os = new ArrayList>(); + List> os = new ArrayList<>(); os.add(Flowable.just("one", "two", "three", "four", "five").subscribeOn(Schedulers.newThread())); os.add(Flowable.just("one", "two", "three", "four", "five").subscribeOn(Schedulers.newThread())); os.add(Flowable.just("one", "two", "three", "four", "five").subscribeOn(Schedulers.newThread())); List expected = Arrays.asList("one", "two", "three", "four", "five", "one", "two", "three", "four", "five", "one", "two", "three", "four", "five"); Iterator iter = Flowable.merge(os, 1).blockingIterable().iterator(); - List actual = new ArrayList(); + List actual = new ArrayList<>(); while (iter.hasNext()) { actual.add(iter.next()); } @@ -57,8 +57,8 @@ public void maxConcurrent() { int maxConcurrent = 2 + (times % 10); AtomicInteger subscriptionCount = new AtomicInteger(0); - List> os = new ArrayList>(); - List scos = new ArrayList(); + List> os = new ArrayList<>(); + List scos = new ArrayList<>(); for (int i = 0; i < observableCount; i++) { SubscriptionCheckObservable sco = new SubscriptionCheckObservable(subscriptionCount, maxConcurrent); scos.add(sco); @@ -66,7 +66,7 @@ public void maxConcurrent() { } Iterator iter = Flowable.merge(os, maxConcurrent).blockingIterable().iterator(); - List actual = new ArrayList(); + List actual = new ArrayList<>(); while (iter.hasNext()) { actual.add(iter.next()); } @@ -118,7 +118,7 @@ public void run() { @Test public void mergeALotOfSourcesOneByOneSynchronously() { int n = 10000; - List> sourceList = new ArrayList>(n); + List> sourceList = new ArrayList<>(n); for (int i = 0; i < n; i++) { sourceList.add(Flowable.just(i)); } @@ -134,7 +134,7 @@ public void mergeALotOfSourcesOneByOneSynchronously() { @Test public void mergeALotOfSourcesOneByOneSynchronouslyTakeHalf() { int n = 10000; - List> sourceList = new ArrayList>(n); + List> sourceList = new ArrayList<>(n); for (int i = 0; i < n; i++) { sourceList.add(Flowable.just(i)); } @@ -150,9 +150,9 @@ public void mergeALotOfSourcesOneByOneSynchronouslyTakeHalf() { @Test public void simple() { for (int i = 1; i < 100; i++) { - TestSubscriberEx ts = new TestSubscriberEx(); - List> sourceList = new ArrayList>(i); - List result = new ArrayList(i); + TestSubscriberEx ts = new TestSubscriberEx<>(); + List> sourceList = new ArrayList<>(i); + List result = new ArrayList<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Flowable.just(j)); result.add(j); @@ -169,9 +169,9 @@ public void simple() { @Test public void simpleOneLess() { for (int i = 2; i < 100; i++) { - TestSubscriberEx ts = new TestSubscriberEx(); - List> sourceList = new ArrayList>(i); - List result = new ArrayList(i); + TestSubscriberEx ts = new TestSubscriberEx<>(); + List> sourceList = new ArrayList<>(i); + List result = new ArrayList<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Flowable.just(j)); result.add(j); @@ -201,9 +201,9 @@ public void simpleAsyncLoop() { @Test public void simpleAsync() { for (int i = 1; i < 50; i++) { - TestSubscriber ts = new TestSubscriber(); - List> sourceList = new ArrayList>(i); - Set expected = new HashSet(i); + TestSubscriber ts = new TestSubscriber<>(); + List> sourceList = new ArrayList<>(i); + Set expected = new HashSet<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Flowable.just(j).subscribeOn(Schedulers.io())); expected.add(j); @@ -213,7 +213,7 @@ public void simpleAsync() { ts.awaitDone(1, TimeUnit.SECONDS); ts.assertNoErrors(); - Set actual = new HashSet(ts.values()); + Set actual = new HashSet<>(ts.values()); assertEquals(expected, actual); } @@ -233,9 +233,9 @@ public void simpleOneLessAsync() { if (System.currentTimeMillis() - t > TimeUnit.SECONDS.toMillis(9)) { break; } - TestSubscriber ts = new TestSubscriber(); - List> sourceList = new ArrayList>(i); - Set expected = new HashSet(i); + TestSubscriber ts = new TestSubscriber<>(); + List> sourceList = new ArrayList<>(i); + Set expected = new HashSet<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Flowable.just(j).subscribeOn(Schedulers.io())); expected.add(j); @@ -245,7 +245,7 @@ public void simpleOneLessAsync() { ts.awaitDone(1, TimeUnit.SECONDS); ts.assertNoErrors(); - Set actual = new HashSet(ts.values()); + Set actual = new HashSet<>(ts.values()); assertEquals(expected, actual); } @@ -253,7 +253,7 @@ public void simpleOneLessAsync() { @Test public void backpressureHonored() throws Exception { - List> sourceList = new ArrayList>(3); + List> sourceList = new ArrayList<>(3); sourceList.add(Flowable.range(0, 100000).subscribeOn(Schedulers.io())); sourceList.add(Flowable.range(0, 100000).subscribeOn(Schedulers.io())); @@ -284,13 +284,13 @@ public void onNext(Integer t) { @Test public void take() throws Exception { - List> sourceList = new ArrayList>(3); + List> sourceList = new ArrayList<>(3); sourceList.add(Flowable.range(0, 100000).subscribeOn(Schedulers.io())); sourceList.add(Flowable.range(0, 100000).subscribeOn(Schedulers.io())); sourceList.add(Flowable.range(0, 100000).subscribeOn(Schedulers.io())); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.merge(sourceList, 2).take(5).subscribe(ts); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java index 22526d1922..57b7a4a667 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -115,7 +115,7 @@ public void mergeArray() { public void mergeList() { final Flowable f1 = Flowable.unsafeCreate(new TestSynchronousFlowable()); final Flowable f2 = Flowable.unsafeCreate(new TestSynchronousFlowable()); - List> listOfFlowables = new ArrayList>(); + List> listOfFlowables = new ArrayList<>(); listOfFlowables.add(f1); listOfFlowables.add(f2); @@ -201,10 +201,10 @@ public void mergeArrayWithThreading() { final TestASynchronousFlowable f2 = new TestASynchronousFlowable(); Flowable m = Flowable.merge(Flowable.unsafeCreate(f1), Flowable.unsafeCreate(f2)); - TestSubscriber ts = new TestSubscriber(stringSubscriber); + TestSubscriber ts = new TestSubscriber<>(stringSubscriber); m.subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); verify(stringSubscriber, never()).onError(any(Throwable.class)); @@ -231,7 +231,7 @@ public void synchronizationOfMultipleSequences() throws Throwable { final AtomicInteger concurrentCounter = new AtomicInteger(); final AtomicInteger totalCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); Flowable m = Flowable.merge(Flowable.unsafeCreate(f1), Flowable.unsafeCreate(f2)); m.subscribe(new DefaultSubscriber() { @@ -424,7 +424,7 @@ public void unsubscribeAsFlowablesComplete() { AtomicBoolean os2 = new AtomicBoolean(false); Flowable f2 = createFlowableOf5IntervalsOf1SecondIncrementsWithSubscriptionHook(scheduler2, os2); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.merge(f1, f2).subscribe(ts); // we haven't incremented time so nothing should be received yet @@ -466,7 +466,7 @@ public void earlyUnsubscribe() { AtomicBoolean os2 = new AtomicBoolean(false); Flowable f2 = createFlowableOf5IntervalsOf1SecondIncrementsWithSubscriptionHook(scheduler2, os2); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.merge(f1, f2).subscribe(ts); // we haven't incremented time so nothing should be received yet @@ -542,7 +542,7 @@ public void concurrency() { for (int i = 0; i < 10; i++) { Flowable merge = Flowable.merge(f.onBackpressureBuffer(), f.onBackpressureBuffer(), f.onBackpressureBuffer()); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); merge.subscribe(ts); ts.awaitDone(3, TimeUnit.SECONDS); @@ -595,10 +595,10 @@ public void run() { for (int i = 0; i < 10; i++) { Flowable merge = Flowable.merge(f, f, f); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); merge.subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertComplete(); List onNextEvents = ts.values(); assertEquals(300, onNextEvents.size()); @@ -642,10 +642,10 @@ public void run() { for (int i = 0; i < 10; i++) { Flowable merge = Flowable.merge(f.onBackpressureBuffer(), f.onBackpressureBuffer(), f.onBackpressureBuffer()); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); merge.subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); ts.assertComplete(); List onNextEvents = ts.values(); @@ -706,7 +706,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1.take(Flowable.bufferSize() * 2), Flowable.just(-99)).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); List onNextEvents = testSubscriber.values(); @@ -752,7 +752,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1.take(Flowable.bufferSize() * 2), f2.take(Flowable.bufferSize() * 2)).observeOn(Schedulers.computation()).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); if (testSubscriber.errors().size() > 0) { testSubscriber.errors().get(0).printStackTrace(); } @@ -795,7 +795,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1).observeOn(Schedulers.computation()).take(Flowable.bufferSize() * 2).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); if (testSubscriber.errors().size() > 0) { testSubscriber.errors().get(0).printStackTrace(); } @@ -850,7 +850,7 @@ public void onNext(Integer t) { }; Flowable.merge(f1).observeOn(Schedulers.computation()).take(Flowable.bufferSize() * 2).subscribe(testSubscriber); - testSubscriber.awaitDone(5, TimeUnit.SECONDS); + testSubscriber.awaitDone(10, TimeUnit.SECONDS); if (testSubscriber.errors().size() > 0) { testSubscriber.errors().get(0).printStackTrace(); } @@ -866,54 +866,54 @@ public void onNext(Integer t) { @Test public void merge1AsyncStreamOf1() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(1, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1, ts.values().size()); } @Test public void merge1AsyncStreamOf1000() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(1, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000, ts.values().size()); } @Test public void merge10AsyncStreamOf1000() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(10, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(10000, ts.values().size()); } @Test public void merge1000AsyncStreamOf1000() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(1000, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @Test public void merge2000AsyncStreamOf100() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(2000, 100).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(200000, ts.values().size()); } @Test public void merge100AsyncStreamOf1() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNAsyncStreamsOfN(100, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(100, ts.values().size()); } @@ -933,45 +933,45 @@ public Flowable apply(Integer i) { @Test public void merge1SyncStreamOf1() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1, ts.values().size()); } @Test public void merge1SyncStreamOf1000000() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1, 1000000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @Test public void merge1000SyncStreamOf1000() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1000, 1000).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @Test public void merge10000SyncStreamOf10() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(10000, 10).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(100000, ts.values().size()); } @Test public void merge1000000SyncStreamOf1() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); mergeNSyncStreamsOfN(1000000, 1).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(1000000, ts.values().size()); } @@ -1016,7 +1016,7 @@ public boolean hasNext() { @Test public void mergeManyAsyncSingle() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable> os = Flowable.range(1, 10000) .map(new Function>() { @@ -1043,7 +1043,7 @@ public void subscribe(Subscriber s) { }); Flowable.merge(os).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(10, TimeUnit.SECONDS); ts.assertNoErrors(); assertEquals(10000, ts.values().size()); } @@ -1051,7 +1051,7 @@ public void subscribe(Subscriber s) { @Test public void shouldCompleteAfterApplyingBackpressure_NormalPath() { Flowable source = Flowable.mergeDelayError(Flowable.just(Flowable.range(1, 2))); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); source.subscribe(subscriber); subscriber.request(3); // 1, 2, - with request(2) we get the 1 and 2 but not the subscriber.assertValues(1, 2); @@ -1061,7 +1061,7 @@ public void shouldCompleteAfterApplyingBackpressure_NormalPath() { @Test public void shouldCompleteAfterApplyingBackpressure_FastPath() { Flowable source = Flowable.mergeDelayError(Flowable.just(Flowable.just(1))); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); source.subscribe(subscriber); subscriber.request(2); // 1, - should work as per .._NormalPath above subscriber.assertValue(1); @@ -1072,7 +1072,7 @@ public void shouldCompleteAfterApplyingBackpressure_FastPath() { public void shouldNotCompleteIfThereArePendingScalarSynchronousEmissionsWhenTheLastInnerSubscriberCompletes() { TestScheduler scheduler = new TestScheduler(); Flowable source = Flowable.mergeDelayError(Flowable.just(1L), Flowable.timer(1, TimeUnit.SECONDS, scheduler).skip(1)); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); source.subscribe(subscriber); scheduler.advanceTimeBy(1, TimeUnit.SECONDS); subscriber.assertNoValues(); @@ -1089,7 +1089,7 @@ public void shouldNotCompleteIfThereArePendingScalarSynchronousEmissionsWhenTheL public void delayedErrorsShouldBeEmittedWhenCompleteAfterApplyingBackpressure_NormalPath() { Throwable exception = new Throwable(); Flowable source = Flowable.mergeDelayError(Flowable.range(1, 2), Flowable.error(exception)); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); source.subscribe(subscriber); subscriber.request(3); // 1, 2, subscriber.assertValues(1, 2); @@ -1101,7 +1101,7 @@ public void delayedErrorsShouldBeEmittedWhenCompleteAfterApplyingBackpressure_No public void delayedErrorsShouldBeEmittedWhenCompleteAfterApplyingBackpressure_FastPath() { Throwable exception = new Throwable(); Flowable source = Flowable.mergeDelayError(Flowable.just(1), Flowable.error(exception)); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); source.subscribe(subscriber); subscriber.request(2); // 1, subscriber.assertValue(1); @@ -1112,7 +1112,7 @@ public void delayedErrorsShouldBeEmittedWhenCompleteAfterApplyingBackpressure_Fa @Test public void shouldNotCompleteWhileThereAreStillScalarSynchronousEmissionsInTheQueue() { Flowable source = Flowable.merge(Flowable.just(1), Flowable.just(2)); - TestSubscriber subscriber = new TestSubscriber(1L); + TestSubscriber subscriber = new TestSubscriber<>(1L); source.subscribe(subscriber); subscriber.assertValue(1); subscriber.request(1); @@ -1123,7 +1123,7 @@ public void shouldNotCompleteWhileThereAreStillScalarSynchronousEmissionsInTheQu public void shouldNotReceivedDelayedErrorWhileThereAreStillScalarSynchronousEmissionsInTheQueue() { Throwable exception = new Throwable(); Flowable source = Flowable.mergeDelayError(Flowable.just(1), Flowable.just(2), Flowable.error(exception)); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); subscriber.request(1); source.subscribe(subscriber); subscriber.assertValue(1); @@ -1137,7 +1137,7 @@ public void shouldNotReceivedDelayedErrorWhileThereAreStillScalarSynchronousEmis public void shouldNotReceivedDelayedErrorWhileThereAreStillNormalEmissionsInTheQueue() { Throwable exception = new Throwable(); Flowable source = Flowable.mergeDelayError(Flowable.range(1, 2), Flowable.range(3, 2), Flowable.error(exception)); - TestSubscriberEx subscriber = new TestSubscriberEx(0L); + TestSubscriberEx subscriber = new TestSubscriberEx<>(0L); subscriber.request(3); source.subscribe(subscriber); subscriber.assertValues(1, 2, 3); @@ -1152,7 +1152,7 @@ public void mergeKeepsRequesting() throws InterruptedException { //for (int i = 0; i < 5000; i++) { //System.out.println(i + "......................................................................."); final CountDownLatch latch = new CountDownLatch(1); - final ConcurrentLinkedQueue messages = new ConcurrentLinkedQueue(); + final ConcurrentLinkedQueue messages = new ConcurrentLinkedQueue<>(); Flowable.range(1, 2) // produce many integers per second @@ -1196,7 +1196,7 @@ public void run() { latch.countDown(); } }).subscribe(); - boolean a = latch.await(2, TimeUnit.SECONDS); + boolean a = latch.await(10, TimeUnit.SECONDS); if (!a) { for (String s : messages) { System.out.println("DEBUG => " + s); @@ -1280,7 +1280,7 @@ public Flowable apply(Integer t) { ; void runMerge(Function> func, TestSubscriberEx ts) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(i); } @@ -1298,12 +1298,12 @@ void runMerge(Function> func, TestSubscriberEx()); + runMerge(toScalar, new TestSubscriberEx<>()); } @Test public void fastMergeHiddenScalar() { - runMerge(toHiddenScalar, new TestSubscriberEx()); + runMerge(toHiddenScalar, new TestSubscriberEx<>()); } @Test @@ -1343,7 +1343,6 @@ public void onNext(Integer t) { } } - @SuppressWarnings("unchecked") @Test public void negativeMaxConcurrent() { try { @@ -1354,7 +1353,6 @@ public void negativeMaxConcurrent() { } } - @SuppressWarnings("unchecked") @Test public void zeroMaxConcurrent() { try { @@ -1466,7 +1464,7 @@ public void flatMapMaxConcurrentJustRange() { public void noInnerReordering() { TestSubscriber ts = TestSubscriber.create(0); FlowableFlatMap.MergeSubscriber, Integer> ms = - new FlowableFlatMap.MergeSubscriber, Integer>(ts, Functions.>identity(), false, 128, 128); + new FlowableFlatMap.MergeSubscriber<>(ts, Functions.>identity(), false, 128, 128); ms.onSubscribe(new BooleanSubscription()); PublishProcessor pp = PublishProcessor.create(); @@ -1488,7 +1486,7 @@ public void noInnerReordering() { public void noOuterScalarReordering() { TestSubscriber ts = TestSubscriber.create(0); FlowableFlatMap.MergeSubscriber, Integer> ms = - new FlowableFlatMap.MergeSubscriber, Integer>(ts, Functions.>identity(), false, 128, 128); + new FlowableFlatMap.MergeSubscriber<>(ts, Functions.>identity(), false, 128, 128); ms.onSubscribe(new BooleanSubscription()); ms.onNext(Flowable.just(1)); @@ -1520,7 +1518,6 @@ public void array() { } } - @SuppressWarnings("unchecked") @Test public void mergeArray2() { Flowable.mergeArray(Flowable.just(1), Flowable.just(2)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletableTest.java index 9008168d48..8eab622ccf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public class FlowableMergeWithCompletableTest extends RxJavaTest { @Test public void normal() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 5).mergeWith( Completable.fromAction(new Action() { @@ -71,7 +71,7 @@ public void cancel() { @Test public void normalBackpressured() { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Flowable.range(1, 5).mergeWith( Completable.fromAction(new Action() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java index 604daa4950..aeef5bb09b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -230,7 +230,7 @@ public void onSuccessFastPathBackpressuredRace() { final PublishProcessor pp = PublishProcessor.create(); final MaybeSubject cs = MaybeSubject.create(); - final TestSubscriber ts = pp.mergeWith(cs).subscribeWith(new TestSubscriber(0)); + final TestSubscriber ts = pp.mergeWith(cs).subscribeWith(new TestSubscriber<>(0)); Runnable r1 = new Runnable() { @Override @@ -259,7 +259,7 @@ public void run() { public void onErrorMainOverflow() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> subscriber = new AtomicReference>(); + final AtomicReference> subscriber = new AtomicReference<>(); TestSubscriber ts = new Flowable() { @Override protected void subscribeActual(Subscriber s) { @@ -448,4 +448,22 @@ public Flowable apply(Flowable upstream) { } }); } + + @Test + public void drainMoreWorkBeforeCancel() { + MaybeSubject ms = MaybeSubject.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.range(1, 5).mergeWith(ms) + .doOnNext(v -> { + if (v == 1) { + ms.onSuccess(6); + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java index 4c5cd09d5b..23612f5754 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableMergeWithSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -87,7 +87,7 @@ public void cancel() { @Test public void normalBackpressured() { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Flowable.range(1, 5).mergeWith( Single.just(100) @@ -226,7 +226,7 @@ public void onSuccessFastPathBackpressuredRace() { final PublishProcessor pp = PublishProcessor.create(); final SingleSubject cs = SingleSubject.create(); - final TestSubscriber ts = pp.mergeWith(cs).subscribeWith(new TestSubscriber(0)); + final TestSubscriber ts = pp.mergeWith(cs).subscribeWith(new TestSubscriber<>(0)); Runnable r1 = new Runnable() { @Override @@ -255,7 +255,7 @@ public void run() { public void onErrorMainOverflow() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> subscriber = new AtomicReference>(); + final AtomicReference> subscriber = new AtomicReference<>(); TestSubscriber ts = new Flowable() { @Override protected void subscribeActual(Subscriber s) { @@ -444,4 +444,22 @@ public Flowable apply(Flowable upstream) { } }); } + + @Test + public void drainMoreWorkBeforeCancel() { + SingleSubject ss = SingleSubject.create(); + + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.range(1, 5).mergeWith(ss) + .doOnNext(v -> { + if (v == 1) { + ss.onSuccess(6); + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java index 624ecee97a..e4e3d6f3fe 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,11 +31,13 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.flowable.FlowableObserveOn.BaseObserveOnSubscriber; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; @@ -65,7 +67,7 @@ public void ordering() throws InterruptedException { Subscriber subscriber = TestHelper.mockSubscriber(); InOrder inOrder = inOrder(subscriber); - TestSubscriberEx ts = new TestSubscriberEx(subscriber); + TestSubscriberEx ts = new TestSubscriberEx<>(subscriber); obs.observeOn(Schedulers.computation()).subscribe(ts); @@ -384,7 +386,7 @@ public void afterUnsubscribeCalledThenObserverOnNextNeverCalled() { final TestScheduler testScheduler = new TestScheduler(); final Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); Flowable.just(1, 2, 3) .observeOn(testScheduler) @@ -519,7 +521,7 @@ public boolean hasNext() { } }); - TestSubscriber testSubscriber = new TestSubscriber(); + TestSubscriber testSubscriber = new TestSubscriber<>(); flowable .take(7) .observeOn(Schedulers.newThread()) @@ -547,7 +549,7 @@ public void subscribe(Subscriber subscriber) { }); - TestSubscriberEx testSubscriber = new TestSubscriberEx(new DefaultSubscriber() { + TestSubscriberEx testSubscriber = new TestSubscriberEx<>(new DefaultSubscriber() { @Override public void onComplete() { @@ -577,20 +579,20 @@ public void onNext(Integer t) { assertEquals(1, errors.size()); System.out.println("Errors: " + errors); Throwable t = errors.get(0); - if (t instanceof MissingBackpressureException) { + if (t instanceof QueueOverflowException) { // success, we expect this } else { - if (t.getCause() instanceof MissingBackpressureException) { + if (t.getCause() instanceof QueueOverflowException) { // this is also okay } else { - fail("Expecting MissingBackpressureException"); + fail("Expecting QueueOverflowException"); } } } @Test public void asyncChild() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, 100000).observeOn(Schedulers.newThread()).observeOn(Schedulers.newThread()).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertNoErrors(); @@ -602,7 +604,7 @@ public void onErrorCutsAheadOfOnNext() { final PublishProcessor processor = PublishProcessor.create(); final AtomicLong counter = new AtomicLong(); - TestSubscriberEx ts = new TestSubscriberEx(new DefaultSubscriber() { + TestSubscriberEx ts = new TestSubscriberEx<>(new DefaultSubscriber() { @Override public void onComplete() { @@ -649,7 +651,7 @@ public void onNext(Long t) { */ @Test public void hotOperatorBackpressure() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.interval(0, 1, TimeUnit.MICROSECONDS) .observeOn(Schedulers.computation()) .map(new Function() { @@ -697,7 +699,7 @@ public void accept(Notification n) { }); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.combineLatest(timer, Flowable. never(), new BiFunction() { @@ -757,7 +759,7 @@ public void onNext(Integer t) { @Test public void noMoreRequestsAfterUnsubscribe() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - final List requests = Collections.synchronizedList(new ArrayList()); + final List requests = Collections.synchronizedList(new ArrayList<>()); Flowable.range(1, 1000000) .doOnRequest(new LongConsumer() { @@ -873,7 +875,7 @@ public void fixedReplenishPattern() { TestScheduler test = new TestScheduler(); - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.range(1, 100) .doOnRequest(new LongConsumer() { @@ -918,9 +920,9 @@ public void bufferSizesWork() { @Test public void synchronousRebatching() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 50) .doOnRequest(new LongConsumer() { @@ -1154,6 +1156,16 @@ public Flowable apply(Flowable f) throws Exception { }); } + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(new Function, Flowable>() { + @Override + public Flowable apply(Flowable f) throws Exception { + return f.observeOn(new TestScheduler()).compose(TestHelper.conditional()); + } + }); + } + @Test public void badSource() { List errors = TestHelper.trackPluginErrors(); @@ -1193,11 +1205,11 @@ public void inputSyncFused() { @Test public void inputAsyncFused() { - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - TestSubscriber ts = us.observeOn(Schedulers.single()).test(); + TestSubscriber ts = up.observeOn(Schedulers.single()).test(); - TestHelper.emit(us, 1, 2, 3, 4, 5); + TestHelper.emit(up, 1, 2, 3, 4, 5); ts .awaitDone(5, TimeUnit.SECONDS) @@ -1206,11 +1218,11 @@ public void inputAsyncFused() { @Test public void inputAsyncFusedError() { - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - TestSubscriber ts = us.observeOn(Schedulers.single()).test(); + TestSubscriber ts = up.observeOn(Schedulers.single()).test(); - us.onError(new TestException()); + up.onError(new TestException()); ts .awaitDone(5, TimeUnit.SECONDS) @@ -1219,11 +1231,11 @@ public void inputAsyncFusedError() { @Test public void inputAsyncFusedErrorDelayed() { - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - TestSubscriber ts = us.observeOn(Schedulers.single(), true).test(); + TestSubscriber ts = up.observeOn(Schedulers.single(), true).test(); - us.onError(new TestException()); + up.onError(new TestException()); ts .awaitDone(5, TimeUnit.SECONDS) @@ -1260,12 +1272,12 @@ public void outputFusedReject() { public void inputOutputAsyncFusedError() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - us.observeOn(Schedulers.single()) + up.observeOn(Schedulers.single()) .subscribe(ts); - us.onError(new TestException()); + up.onError(new TestException()); ts .awaitDone(5, TimeUnit.SECONDS) @@ -1280,12 +1292,12 @@ public void inputOutputAsyncFusedError() { public void inputOutputAsyncFusedErrorDelayed() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - us.observeOn(Schedulers.single(), true) + up.observeOn(Schedulers.single(), true) .subscribe(ts); - us.onError(new TestException()); + up.onError(new TestException()); ts .awaitDone(5, TimeUnit.SECONDS) @@ -1298,11 +1310,11 @@ public void inputOutputAsyncFusedErrorDelayed() { @Test public void outputFusedCancelReentrant() throws Exception { - final UnicastProcessor us = UnicastProcessor.create(); + final UnicastProcessor up = UnicastProcessor.create(); final CountDownLatch cdl = new CountDownLatch(1); - us.observeOn(Schedulers.single()) + up.observeOn(Schedulers.single()) .subscribe(new FlowableSubscriber() { Subscription upstream; int count; @@ -1315,7 +1327,7 @@ public void onSubscribe(Subscription s) { @Override public void onNext(Integer value) { if (++count == 1) { - us.onNext(2); + up.onNext(2); upstream.cancel(); cdl.countDown(); } @@ -1332,7 +1344,7 @@ public void onComplete() { } }); - us.onNext(1); + up.onNext(1); cdl.await(); } @@ -1594,7 +1606,7 @@ public void onNext(Integer t) { @Test public void syncFusedCancelAfterRequest2() { - final TestSubscriber ts = new TestSubscriber(2L); + final TestSubscriber ts = new TestSubscriber<>(2L); Flowable.range(1, 2) .observeOn(Schedulers.single()) @@ -1630,7 +1642,7 @@ public void onNext(Integer t) { @Test public void syncFusedCancelAfterRequestConditional2() { - final TestSubscriber ts = new TestSubscriber(2L); + final TestSubscriber ts = new TestSubscriber<>(2L); Flowable.range(1, 2) .observeOn(Schedulers.single()) @@ -1644,7 +1656,7 @@ public void syncFusedCancelAfterRequestConditional2() { @Test public void nonFusedCancelAfterRequestConditional2() { - final TestSubscriber ts = new TestSubscriber(2L); + final TestSubscriber ts = new TestSubscriber<>(2L); Flowable.range(1, 2).hide() .observeOn(Schedulers.single()) @@ -1806,7 +1818,7 @@ public boolean isDisposed() { public Disposable schedule(Runnable run, long delay, TimeUnit unit) { run.run(); - return Disposables.empty(); + return Disposable.empty(); } } } @@ -1988,4 +2000,120 @@ public void fusedParallelProcessing() { .assertComplete() .assertNoErrors(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().observeOn(ImmediateThinScheduler.INSTANCE)); + } + + @Test + public void syncFusedCancelAfterPoll() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .map(v -> { + ts.cancel(); + return v + 1; + }) + .compose(TestHelper.flowableStripBoundary()) + .observeOn(ImmediateThinScheduler.INSTANCE) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void syncFusedCancelAfterPollConditional() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1) + .map(v -> { + ts.cancel(); + return v + 1; + }) + .compose(TestHelper.flowableStripBoundary()) + .observeOn(ImmediateThinScheduler.INSTANCE) + .compose(TestHelper.conditional()) + .subscribe(ts); + + ts.assertEmpty(); + } + + @Test + public void backFusedMoreWork() { + final TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } + + @Test + public void moreWorkInRunAsync() { + final TestSubscriberEx ts = new TestSubscriberEx<>(); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } + + @Test + public void backFusedConditionalMoreWork() { + final TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .compose(TestHelper.conditional()) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } + + @Test + public void conditionalMoreWorkInRunAsync() { + final TestSubscriberEx ts = new TestSubscriberEx<>(); + + PublishProcessor pp = PublishProcessor.create(); + + pp.observeOn(ImmediateThinScheduler.INSTANCE) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + } + }) + .compose(TestHelper.conditional()) + .subscribe(ts); + + pp.onNext(1); + + ts.assertValuesOnly(1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java index 91a2b3b7c5..217162c206 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferStrategyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,7 +15,9 @@ import static io.reactivex.rxjava3.core.BackpressureOverflowStrategy.*; import static io.reactivex.rxjava3.internal.functions.Functions.EMPTY_ACTION; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -28,8 +30,9 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableOnBackpressureBufferStrategyTest extends RxJavaTest { @@ -58,7 +61,7 @@ public void run() throws Exception { } private TestSubscriber createTestSubscriber() { - return new TestSubscriber(new DefaultSubscriber() { + return new TestSubscriber<>(new DefaultSubscriber() { @Override protected void onStart() { @@ -206,4 +209,117 @@ public void justTake() { .test() .assertResult(1); } + + @Test + public void overflowNullAction() { + Flowable.range(1, 5) + .onBackpressureBuffer(1, null, BackpressureOverflowStrategy.DROP_OLDEST) + .test(0L) + .assertEmpty(); + } + + @Test + public void cancelOnDrain() { + Flowable.range(1, 5) + .onBackpressureBuffer(10, null, BackpressureOverflowStrategy.DROP_OLDEST) + .takeUntil(v -> true) + .test(0L) + .assertEmpty() + .requestMore(10) + .assertResult(1); + } + + @Test + public void onDroppedNormalDropOldest() throws Throwable { + PublishProcessor pp = PublishProcessor.create(); + + @SuppressWarnings("unchecked") + Consumer onDropped = mock(Consumer.class); + + TestSubscriber ts = pp.onBackpressureBuffer(1, null, BackpressureOverflowStrategy.DROP_OLDEST, onDropped) + .test(0L); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertEmpty(); + verify(onDropped, never()).accept(any()); + + pp.onNext(2); + + ts.assertEmpty(); + + verify(onDropped).accept(1); + } + + @Test + public void onDroppedNormalDropLatest() throws Throwable { + PublishProcessor pp = PublishProcessor.create(); + + @SuppressWarnings("unchecked") + Consumer onDropped = mock(Consumer.class); + + TestSubscriber ts = pp.onBackpressureBuffer(2, null, BackpressureOverflowStrategy.DROP_LATEST, onDropped) + .test(0L); + + ts.assertEmpty(); + + pp.onNext(1); + + pp.onNext(2); + + ts.assertEmpty(); + verify(onDropped, never()).accept(any()); + + pp.onNext(3); + + ts.assertEmpty(); + + verify(onDropped).accept(2); + } + + @Test + public void onDroppedNormalError() throws Throwable { + PublishProcessor pp = PublishProcessor.create(); + + @SuppressWarnings("unchecked") + Consumer onDropped = mock(Consumer.class); + + TestSubscriber ts = pp.onBackpressureBuffer(1, null, BackpressureOverflowStrategy.ERROR, onDropped) + .test(0L); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertEmpty(); + verify(onDropped, never()).accept(any()); + + pp.onNext(2); + + ts.assertFailure(MissingBackpressureException.class); + + verify(onDropped).accept(2); + } + + @Test + public void onDroppedCrash() throws Throwable { + PublishProcessor pp = PublishProcessor.create(); + + Consumer onDropped = v -> { throw new TestException(); }; + + TestSubscriberEx ts = pp.onBackpressureBuffer(1, null, BackpressureOverflowStrategy.DROP_OLDEST, onDropped) + .subscribeWith(new TestSubscriberEx(0L)); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java index 3d32d2c808..657ce36f1b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureBufferTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,6 +14,8 @@ package io.reactivex.rxjava3.internal.operators.flowable; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import java.util.List; import java.util.concurrent.*; @@ -26,9 +28,9 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -39,7 +41,7 @@ public class FlowableOnBackpressureBufferTest extends RxJavaTest { @Test public void noBackpressureSupport() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); // this will be ignored ts.request(100); // we take 500 so it unsubscribes @@ -53,7 +55,7 @@ public void noBackpressureSupport() { public void fixBackpressureWithBuffer() throws InterruptedException { final CountDownLatch l1 = new CountDownLatch(100); final CountDownLatch l2 = new CountDownLatch(150); - TestSubscriber ts = new TestSubscriber(new DefaultSubscriber() { + TestSubscriber ts = new TestSubscriber<>(new DefaultSubscriber() { @Override protected void onStart() { @@ -110,17 +112,19 @@ public void fixBackpressureBufferZeroCapacity() throws InterruptedException { public void fixBackpressureBoundedBuffer() throws InterruptedException { final CountDownLatch l1 = new CountDownLatch(100); final CountDownLatch backpressureCallback = new CountDownLatch(1); - TestSubscriber ts = new TestSubscriber(new DefaultSubscriber() { + TestSubscriber ts = new TestSubscriber<>(new DefaultSubscriber() { @Override protected void onStart() { } @Override - public void onComplete() { } + public void onComplete() { + } @Override - public void onError(Throwable e) { } + public void onError(Throwable e) { + } @Override public void onNext(Long t) { @@ -213,14 +217,6 @@ public void fixBackpressureBufferZeroCapacity2() throws InterruptedException { Flowable.empty().onBackpressureBuffer(0); } - @Test(expected = NullPointerException.class) - public void fixBackpressureBufferNullStrategy() throws InterruptedException { - Flowable.empty().onBackpressureBuffer(10, new Action() { - @Override - public void run() { } - }, null); - } - @Test public void noDelayError() { @@ -346,4 +342,60 @@ public void fusedNoConcurrentCleanDueToCancel() { } } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onBackpressureBuffer()); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().onBackpressureBuffer()); + } + + @Test + public void onDroppedNormal() throws Throwable { + PublishProcessor pp = PublishProcessor.create(); + + @SuppressWarnings("unchecked") + Consumer onDropped = mock(Consumer.class); + + TestSubscriber ts = pp.onBackpressureBuffer(1, false, false, () -> { }, onDropped) + .test(0L); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertEmpty(); + verify(onDropped, never()).accept(any()); + + pp.onNext(2); + + ts.assertFailure(MissingBackpressureException.class); + + verify(onDropped).accept(2); + } + + @Test + public void onDroppedCrash() throws Throwable { + PublishProcessor pp = PublishProcessor.create(); + + Consumer onDropped = v -> { throw new TestException(); }; + + TestSubscriberEx ts = pp.onBackpressureBuffer(1, false, false, () -> { }, onDropped) + .subscribeWith(new TestSubscriberEx(0L)); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertFailure(MissingBackpressureException.class); + + assertTrue(ts.errors().get(0).getCause() instanceof TestException); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDropTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDropTest.java index dabf934ce3..0dfec9422c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDropTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureDropTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public class FlowableOnBackpressureDropTest extends RxJavaTest { @Test public void noBackpressureSupport() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); // this will be ignored ts.request(100); // we take 500 so it unsubscribes @@ -44,7 +44,7 @@ public void noBackpressureSupport() { @Test public void withObserveOn() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, Flowable.bufferSize() * 10).onBackpressureDrop().observeOn(Schedulers.io()).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); } @@ -53,7 +53,7 @@ public void withObserveOn() throws InterruptedException { public void fixBackpressureWithBuffer() throws InterruptedException { final CountDownLatch l1 = new CountDownLatch(100); final CountDownLatch l2 = new CountDownLatch(150); - TestSubscriber ts = new TestSubscriber(new DefaultSubscriber() { + TestSubscriber ts = new TestSubscriber<>(new DefaultSubscriber() { @Override protected void onStart() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java index a2288abcb1..2693bf3014 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureErrorTest.java @@ -1,11 +1,11 @@ -/** +/* * 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. @@ -13,11 +13,16 @@ package io.reactivex.rxjava3.internal.operators.flowable; +import static org.junit.Assert.*; + import org.junit.Test; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.MissingBackpressureException; import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; public class FlowableOnBackpressureErrorTest extends RxJavaTest { @@ -37,7 +42,7 @@ public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowable(new Function, Publisher>() { @Override public Publisher apply(Flowable f) throws Exception { - return new FlowableOnBackpressureError(f); + return new FlowableOnBackpressureError<>(f); } }); } @@ -47,8 +52,24 @@ public void badSource() { TestHelper.checkBadSourceFlowable(new Function, Object>() { @Override public Object apply(Flowable f) throws Exception { - return new FlowableOnBackpressureError(f); + return new FlowableOnBackpressureError<>(f); } }, false, 1, 1, 1); } + + @Test + public void overflowCancels() { + PublishSubject ps = PublishSubject.create(); + + TestSubscriber ts = ps.toFlowable(BackpressureStrategy.ERROR) + .test(0L); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + assertFalse(ps.hasObservers()); + + ts.assertFailure(MissingBackpressureException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java index bb52136b0e..f0d50994ed 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit; import org.junit.*; +import org.mockito.InOrder; import org.reactivestreams.Publisher; import io.reactivex.rxjava3.core.*; @@ -27,10 +28,12 @@ import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; +import static org.mockito.Mockito.inOrder; + public class FlowableOnBackpressureLatestTest extends RxJavaTest { @Test public void simple() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(1, 5).onBackpressureLatest().subscribe(ts); @@ -41,7 +44,7 @@ public void simple() { @Test public void simpleError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) .onBackpressureLatest().subscribe(ts); @@ -53,7 +56,7 @@ public void simpleError() { @Test public void simpleBackpressure() { - TestSubscriber ts = new TestSubscriber(2L); + TestSubscriber ts = new TestSubscriber<>(2L); Flowable.range(1, 5).onBackpressureLatest().subscribe(ts); @@ -62,10 +65,72 @@ public void simpleBackpressure() { ts.assertNotComplete(); } + @Test + public void simpleBackpressureWithOnDroppedCallback() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + Observer dropCallbackObserver = TestHelper.mockObserver(); + + source.onBackpressureLatest(dropCallbackObserver::onNext) + .subscribe(ts); + + ts.assertNoValues(); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + ts.request(1); + + ts.assertValues(3); + + source.onNext(4); + source.onNext(5); + + ts.request(2); + + ts.assertValues(3,5); + + InOrder dropCallbackOrder = inOrder(dropCallbackObserver); + dropCallbackOrder.verify(dropCallbackObserver).onNext(1); + dropCallbackOrder.verify(dropCallbackObserver).onNext(2); + dropCallbackOrder.verify(dropCallbackObserver).onNext(4); + dropCallbackOrder.verifyNoMoreInteractions(); + } + + @Test + public void simpleBackpressureWithOnDroppedCallbackEx() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureLatest(e -> { + if (e == 3) { + throw new TestException("forced"); + } + }) + .subscribe(ts); + + ts.assertNoValues(); + + source.onNext(1); + source.onNext(2); + + ts.request(1); + + ts.assertValues(2); + + source.onNext(3); + source.onNext(4); + + ts.assertError(TestException.class); + ts.assertValues(2); + } + @Test public void synchronousDrop() { PublishProcessor source = PublishProcessor.create(); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); source.onBackpressureLatest().subscribe(ts); @@ -105,7 +170,7 @@ public void synchronousDrop() { } @Test - public void asynchronousDrop() throws InterruptedException { + public void asynchronousDrop() { TestSubscriberEx ts = new TestSubscriberEx(1L) { final Random rnd = new Random(); @Override @@ -133,6 +198,12 @@ public void onNext(Integer t) { int n = ts.values().size(); System.out.println("testAsynchronousDrop -> " + n); Assert.assertTrue("All events received?", n < m); + int previous = 0; + for (Integer current : ts.values()) { + Assert.assertTrue("The sequence must be increasing [current value=" + previous + + ", previous value=" + current + "]", previous <= current); + previous = current; + } } @Test diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java new file mode 100644 index 0000000000..5f7b7ad957 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceTest.java @@ -0,0 +1,261 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.TestSubscriberEx; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class FlowableOnBackpressureReduceTest extends RxJavaTest { + + static final BiFunction TEST_INT_REDUCER = (previous, current) -> previous + current + 50; + + static final BiFunction TEST_OBJECT_REDUCER = (previous, current) -> current; + + @Test + public void simple() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertNoErrors(); + ts.assertTerminated(); + ts.assertValues(1, 2, 3, 4, 5); + } + + @Test + public void simpleError() { + TestSubscriberEx ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) + .onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertTerminated(); + ts.assertError(TestException.class); + ts.assertValues(1, 2, 3, 4, 5); + } + + @Test + public void simpleBackpressure() { + TestSubscriber ts = new TestSubscriber<>(2L); + + Flowable.range(1, 5).onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertNoErrors(); + ts.assertValues(1, 2); + ts.assertNotComplete(); + } + + @Test + public void synchronousDrop() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(TEST_INT_REDUCER).subscribe(ts); + + ts.assertNoValues(); + + source.onNext(1); + ts.request(2); + + ts.assertValue(1); + + source.onNext(2); + + ts.assertValues(1, 2); + + source.onNext(3); + source.onNext(4); //3 + 4 + 50 == 57 + source.onNext(5); //57 + 5 + 50 == 112 + source.onNext(6); //112 + 6 + 50 == 168 + + ts.request(2); + + ts.assertValues(1, 2, 168); + + source.onNext(7); + + ts.assertValues(1, 2, 168, 7); + + source.onNext(8); + source.onNext(9); //8 + 9 + 50 == 67 + source.onComplete(); + + ts.request(1); + + ts.assertValues(1, 2, 168, 7, 67); + ts.assertNoErrors(); + ts.assertTerminated(); + } + + @Test + public void reduceBackpressuredSync() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(Integer::sum).subscribe(ts); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + ts.request(1); + + ts.assertValuesOnly(6); + + source.onNext(4); + source.onComplete(); + + ts.assertValuesOnly(6); + + ts.request(1); + ts.assertResult(6, 4); + } + + private TestSubscriberEx createDelayedSubscriber() { + return new TestSubscriberEx(1L) { + final Random rnd = new Random(); + + @Override + public void onNext(T t) { + super.onNext(t); + if (rnd.nextDouble() < 0.001) { + try { + Thread.sleep(1); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + request(1); + } + }; + } + + private void assertValuesDropped(TestSubscriberEx ts, int totalValues) { + int n = ts.values().size(); + System.out.println("testAsynchronousDrop -> " + n); + Assert.assertTrue("All events received?", n < totalValues); + } + + private void assertIncreasingSequence(TestSubscriberEx ts) { + int previous = 0; + for (Integer current : ts.values()) { + Assert.assertTrue("The sequence must be increasing [current value=" + previous + + ", previous value=" + current + "]", previous <= current); + previous = current; + } + } + + @Test + public void asynchronousDrop() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.range(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce((previous, current) -> { + //in that case it works like onBackpressureLatest + //the output sequence of number must be increasing + return current; + }) + .observeOn(Schedulers.io()) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + assertIncreasingSequence(ts); + } + + @Test + public void asynchronousDrop2() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.rangeLong(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce(Long::sum) + .observeOn(Schedulers.io()) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + long sum = 0; + for (Long i : ts.values()) { + sum += i; + } + //sum = (A1 + An) * n / 2 = 100_001 * 50_000 = 50_000_00000 + 50_000 = 50_000_50_000 + Assert.assertEquals("Wrong sum: " + sum, 5000050000L, sum); + } + + @Test + public void nullPointerFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0); + source.onBackpressureReduce((l, r) -> null).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, NullPointerException.class, "The reducer returned a null value"); + } + + @Test + public void exceptionFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0); + source.onBackpressureReduce((l, r) -> { + throw new TestException("Test exception"); + }).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, TestException.class, "Test exception"); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onBackpressureReduce(TEST_OBJECT_REDUCER)); + } + + @Test + public void take() { + Flowable.just(1, 2) + .onBackpressureReduce(TEST_INT_REDUCER) + .take(1) + .test() + .assertResult(1); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Flowable.never().onBackpressureReduce(TEST_OBJECT_REDUCER)); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().onBackpressureReduce(TEST_OBJECT_REDUCER)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java new file mode 100644 index 0000000000..32cee35603 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureReduceWithTest.java @@ -0,0 +1,331 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.BiFunction; +import io.reactivex.rxjava3.functions.Supplier; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.TestSubscriberEx; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class FlowableOnBackpressureReduceWithTest extends RxJavaTest { + + private static BiFunction, T, List> createTestReducer() { + return (list, number) -> { + list.add(number); + return list; + }; + } + + private static Supplier> createTestSupplier() { + return ArrayList::new; + } + + @Test + public void simple() { + TestSubscriberEx> ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertNoErrors(); + ts.assertTerminated(); + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Collections.singletonList(3), + Collections.singletonList(4), + Collections.singletonList(5) + ); + } + + @Test + public void simpleError() { + TestSubscriberEx> ts = new TestSubscriberEx<>(); + + Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) + .onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertTerminated(); + ts.assertError(TestException.class); + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Collections.singletonList(3), + Collections.singletonList(4), + Collections.singletonList(5) + ); + } + + @Test + public void simpleBackpressure() { + TestSubscriberEx> ts = new TestSubscriberEx<>(2L); + + Flowable.range(1, 5).onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertNoErrors(); + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2) + ); + ts.assertNotComplete(); + } + + @Test + public void reduceBackpressuredSync() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(() -> 0, Integer::sum).subscribe(ts); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + ts.request(1); + + ts.assertValuesOnly(6); + + source.onNext(4); + source.onComplete(); + + ts.assertValuesOnly(6); + + ts.request(1); + ts.assertResult(6, 4); + } + + @Test + public void synchronousDrop() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + + source.onBackpressureReduce(createTestSupplier(), createTestReducer()).subscribe(ts); + + ts.assertNoValues(); + + source.onNext(1); + ts.request(2); + + ts.assertValues(Collections.singletonList(1)); + + source.onNext(2); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2) + ); + + source.onNext(3); + source.onNext(4); + source.onNext(5); + source.onNext(6); + + ts.request(2); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Arrays.asList(3, 4, 5, 6) + ); + + source.onNext(7); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Arrays.asList(3, 4, 5, 6), + Collections.singletonList(7) + ); + + source.onNext(8); + source.onNext(9); + source.onComplete(); + + ts.request(1); + + ts.assertValues( + Collections.singletonList(1), + Collections.singletonList(2), + Arrays.asList(3, 4, 5, 6), + Collections.singletonList(7), + Arrays.asList(8, 9) + ); + ts.assertNoErrors(); + ts.assertTerminated(); + } + + private TestSubscriberEx createDelayedSubscriber() { + return new TestSubscriberEx(1L) { + final Random rnd = new Random(); + + @Override + public void onNext(T t) { + super.onNext(t); + if (rnd.nextDouble() < 0.001) { + try { + Thread.sleep(1); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + request(1); + } + }; + } + + private void assertValuesDropped(TestSubscriberEx ts, int totalValues) { + int n = ts.values().size(); + System.out.println("testAsynchronousDrop -> " + n); + Assert.assertTrue("All events received?", n < totalValues); + } + + private void assertIncreasingSequence(TestSubscriberEx ts) { + int previous = 0; + for (Integer current : ts.values()) { + Assert.assertTrue("The sequence must be increasing [current value=" + previous + + ", previous value=" + current + "]", previous <= current); + previous = current; + } + } + + @Test + public void asynchronousDrop() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.range(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce((Supplier>) Collections::emptyList, (list, current) -> { + //in that case it works like onBackpressureLatest + //the output sequence of number must be increasing + return Collections.singletonList(current); + }) + .observeOn(Schedulers.io()) + .concatMap(Flowable::fromIterable) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + assertIncreasingSequence(ts); + } + + @Test + public void asynchronousDrop2() { + TestSubscriberEx ts = createDelayedSubscriber(); + int m = 100000; + Flowable.rangeLong(1, m) + .subscribeOn(Schedulers.computation()) + .onBackpressureReduce(createTestSupplier(), createTestReducer()) + .observeOn(Schedulers.io()) + .concatMap(list -> Flowable.just(list.stream().reduce(Long::sum).orElseThrow(() -> { + throw new IllegalArgumentException("No value in list"); + }))) + .subscribe(ts); + + ts.awaitDone(2, TimeUnit.SECONDS); + ts.assertTerminated(); + assertValuesDropped(ts, m); + long sum = 0; + for (Long i : ts.values()) { + sum += i; + } + //sum = (A1 + An) * n / 2 = 100_001 * 50_000 = 50_000_00000 + 50_000 = 50_000_50_000 + Assert.assertEquals("Wrong sum: " + sum, 5000050000L, sum); + } + + @Test + public void nullPointerFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(createTestSupplier(), (BiFunction, ? super Integer, List>) (list, number) -> null).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, NullPointerException.class, "The reducer returned a null value"); + } + + @Test + public void nullPointerFromSupplier() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(() -> null, createTestReducer()).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, NullPointerException.class, "The supplier returned a null value"); + } + + @Test + public void exceptionFromReducer() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(createTestSupplier(), (BiFunction, ? super Integer, List>) (l, r) -> { + throw new TestException("Test exception"); + }).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, TestException.class, "Test exception"); + } + + @Test + public void exceptionFromSupplier() { + PublishProcessor source = PublishProcessor.create(); + TestSubscriberEx> ts = new TestSubscriberEx<>(0L); + source.onBackpressureReduce(() -> { + throw new TestException("Test exception"); + }, createTestReducer()).subscribe(ts); + + source.onNext(1); + source.onNext(2); + + TestHelper.assertError(ts.errors(), 0, TestException.class, "Test exception"); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onBackpressureReduce(createTestSupplier(), createTestReducer())); + } + + @Test + public void take() { + Flowable.just(1, 2) + .onBackpressureReduce(createTestSupplier(), createTestReducer()) + .take(1) + .test() + .assertResult(Collections.singletonList(1)); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Flowable.never().onBackpressureReduce(createTestSupplier(), createTestReducer())); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().onBackpressureReduce(createTestSupplier(), createTestReducer())); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorCompleteTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorCompleteTest.java new file mode 100644 index 0000000000..0a6f137ce2 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorCompleteTest.java @@ -0,0 +1,143 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.junit.Assert.*; + +import java.io.IOException; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subscribers.TestSubscriber; +import io.reactivex.rxjava3.testsupport.*; + +public class FlowableOnErrorCompleteTest { + + @Test + public void normal() { + Flowable.range(1, 10) + .onErrorComplete() + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void normalBackpressured() { + Flowable.range(1, 10) + .onErrorComplete() + .test(0) + .assertEmpty() + .requestMore(3) + .assertValuesOnly(1, 2, 3) + .requestMore(3) + .assertValuesOnly(1, 2, 3, 4, 5, 6) + .requestMore(4) + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void empty() { + Flowable.empty() + .onErrorComplete() + .test() + .assertResult(); + } + + @Test + public void error() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable.error(new TestException()) + .onErrorComplete() + .test() + .assertResult(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorMatches() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable.error(new TestException()) + .onErrorComplete(error -> error instanceof TestException) + .test() + .assertResult(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorNotMatches() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable.error(new IOException()) + .onErrorComplete(error -> error instanceof TestException) + .test() + .assertFailure(IOException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorPredicateCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestSubscriberEx ts = Flowable.error(new IOException()) + .onErrorComplete(error -> { throw new TestException(); }) + .subscribeWith(new TestSubscriberEx<>()) + .assertFailure(CompositeException.class); + + TestHelper.assertError(ts, 0, IOException.class); + TestHelper.assertError(ts, 1, TestException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void itemsThenError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable.range(1, 5) + .map(v -> 4 / (3 - v)) + .onErrorComplete() + .test() + .assertResult(2, 4); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void cancel() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp + .onErrorComplete() + .test(); + + assertTrue("No subscribers?!", pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Still subscribers?!", pp.hasSubscribers()); + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.onErrorComplete()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFlowableTest.java index 742b1e5853..8b94b0d6f0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -148,7 +148,7 @@ public void run() { @Test public void backpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, 100000) .onErrorResumeWith(Flowable.just(1)) .observeOn(Schedulers.computation()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFunctionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFunctionTest.java index 72a0bf0161..982ffecd1d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFunctionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorResumeNextViaFunctionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public class FlowableOnErrorResumeNextViaFunctionTest extends RxJavaTest { @Test public void resumeNextWithSynchronousExecution() { - final AtomicReference receivedException = new AtomicReference(); + final AtomicReference receivedException = new AtomicReference<>(); Flowable w = Flowable.unsafeCreate(new Publisher() { @Override @@ -79,7 +79,7 @@ public Flowable apply(Throwable t1) { @Test public void resumeNextWithAsyncExecution() { - final AtomicReference receivedException = new AtomicReference(); + final AtomicReference receivedException = new AtomicReference<>(); Subscription s = mock(Subscription.class); TestFlowable w = new TestFlowable(s, "one"); Function> resume = new Function>() { @@ -176,7 +176,7 @@ public Flowable apply(Throwable t1) { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber, Long.MAX_VALUE); + TestSubscriber ts = new TestSubscriber<>(subscriber, Long.MAX_VALUE); flowable.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -228,7 +228,7 @@ public void run() { @Test public void backpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, 100000) .onErrorResumeNext(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturnTest.java index 324cfe0eec..15007f0228 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableOnErrorReturnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public class FlowableOnErrorReturnTest extends RxJavaTest { public void resumeNext() { TestFlowable f = new TestFlowable("one"); Flowable w = Flowable.unsafeCreate(f); - final AtomicReference capturedException = new AtomicReference(); + final AtomicReference capturedException = new AtomicReference<>(); Flowable flowable = w.onErrorReturn(new Function() { @@ -74,7 +74,7 @@ public String apply(Throwable e) { public void functionThrowsError() { TestFlowable f = new TestFlowable("one"); Flowable w = Flowable.unsafeCreate(f); - final AtomicReference capturedException = new AtomicReference(); + final AtomicReference capturedException = new AtomicReference<>(); Flowable flowable = w.onErrorReturn(new Function() { @@ -132,7 +132,7 @@ public String apply(Throwable t1) { }); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber, Long.MAX_VALUE); + TestSubscriber ts = new TestSubscriber<>(subscriber, Long.MAX_VALUE); flowable.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -146,7 +146,7 @@ public String apply(Throwable t1) { @Test public void backpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(0, 100000) .onErrorReturn(new Function() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java index f62616097e..5511fd0123 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishFunctionTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.flowable; @@ -19,6 +16,7 @@ import static org.junit.Assert.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.junit.*; @@ -26,7 +24,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.processors.PublishProcessor; @@ -37,14 +34,11 @@ public class FlowablePublishFunctionTest extends RxJavaTest { @Test public void concatTakeFirstLastCompletes() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - Flowable.range(1, 3).publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return Flowable.concat(f.take(5), f.takeLast(5)); - } - }).subscribe(ts); + Flowable.range(1, 3) + .publish(f -> Flowable.concat(f.take(5), f.takeLast(5))) + .subscribe(ts); ts.assertValues(1, 2, 3); ts.assertNoErrors(); @@ -55,12 +49,9 @@ public Flowable apply(Flowable f) { public void concatTakeFirstLastBackpressureCompletes() { TestSubscriber ts = TestSubscriber.create(0L); - Flowable.range(1, 6).publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return Flowable.concat(f.take(5), f.takeLast(5)); - } - }).subscribe(ts); + Flowable.range(1, 6) + .publish(f -> Flowable.concat(f.take(5), f.takeLast(5))) + .subscribe(ts); ts.assertNoValues(); ts.assertNoErrors(); @@ -86,12 +77,7 @@ public void canBeCancelled() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return Flowable.concat(f.take(5), f.takeLast(5)); - } - }).subscribe(ts); + pp.publish(f -> Flowable.concat(f.take(5), f.takeLast(5))).subscribe(ts); pp.onNext(1); pp.onNext(2); @@ -108,8 +94,7 @@ public Flowable apply(Flowable f) { @Test public void invalidPrefetch() { try { - Flowable.never().publish( - Functions.>identity(), -99); + Flowable.never().publish(Functions.identity(), -99); fail("Didn't throw IllegalArgumentException"); } catch (IllegalArgumentException ex) { Assert.assertEquals("prefetch > 0 required but it was -99", ex.getMessage()); @@ -122,12 +107,7 @@ public void takeCompletes() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f.take(1); - } - }).subscribe(ts); + pp.publish(f -> f.take(1)).subscribe(ts); pp.onNext(1); @@ -153,12 +133,7 @@ public void onStart() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f.take(1); - } - }).subscribe(ts); + pp.publish(f -> f.take(1)).subscribe(ts); Assert.assertEquals(1, startCount.get()); } @@ -169,12 +144,7 @@ public void takeCompletesUnsafe() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f.take(1); - } - }).subscribe(ts); + pp.publish(f -> f.take(1)).subscribe(ts); pp.onNext(1); @@ -191,12 +161,7 @@ public void directCompletesUnsafe() { PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f; - } - }).subscribe(ts); + pp.publish(Functions.identity()).subscribe(ts); pp.onNext(1); pp.onComplete(); @@ -210,16 +175,11 @@ public Flowable apply(Flowable f) { @Test public void overflowMissingBackpressureException() { - TestSubscriberEx ts = new TestSubscriberEx(0); + TestSubscriberEx ts = new TestSubscriberEx<>(0); PublishProcessor pp = PublishProcessor.create(); - pp.publish(new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f; - } - }).subscribe(ts); + pp.publish(Functions.identity()).subscribe(ts); for (int i = 0; i < Flowable.bufferSize() * 2; i++) { pp.onNext(i); @@ -229,23 +189,18 @@ public Flowable apply(Flowable f) { ts.assertError(MissingBackpressureException.class); ts.assertNotComplete(); - Assert.assertEquals("Could not emit value due to lack of requests", + Assert.assertEquals(MissingBackpressureException.DEFAULT_MESSAGE, ts.errors().get(0).getMessage()); Assert.assertFalse("Source has subscribers?", pp.hasSubscribers()); } @Test public void overflowMissingBackpressureExceptionDelayed() { - TestSubscriberEx ts = new TestSubscriberEx(0); + TestSubscriberEx ts = new TestSubscriberEx<>(0); PublishProcessor pp = PublishProcessor.create(); - new FlowablePublishMulticast(pp, new Function, Flowable>() { - @Override - public Flowable apply(Flowable f) { - return f; - } - }, Flowable.bufferSize(), true).subscribe(ts); + new FlowablePublishMulticast<>(pp, Functions.identity(), Flowable.bufferSize(), true).subscribe(ts); for (int i = 0; i < Flowable.bufferSize() * 2; i++) { pp.onNext(i); @@ -257,29 +212,23 @@ public Flowable apply(Flowable f) { ts.assertError(MissingBackpressureException.class); ts.assertNotComplete(); - Assert.assertEquals("Could not emit value due to lack of requests", ts.errors().get(0).getMessage()); + Assert.assertEquals(MissingBackpressureException.DEFAULT_MESSAGE, ts.errors().get(0).getMessage()); Assert.assertFalse("Source has subscribers?", pp.hasSubscribers()); } @Test public void emptyIdentityMapped() { Flowable.empty() - .publish(Functions.>identity()) + .publish(Functions.identity()) .test() - .assertResult() - ; + .assertResult(); } @Test public void independentlyMapped() { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = pp.publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.range(1, 5); - } - }).test(0); + TestSubscriber ts = pp.publish(v -> Flowable.range(1, 5)).test(0); assertTrue("pp has no Subscribers?!", pp.hasSubscribers()); @@ -296,12 +245,7 @@ public Publisher apply(Flowable v) throws Exception { @Test public void badSource() { - TestHelper.checkBadSourceFlowable(new Function, Object>() { - @Override - public Object apply(Flowable f) throws Exception { - return f.publish(Functions.>identity()); - } - }, false, 1, 1, 1); + TestHelper.checkBadSourceFlowable(f -> f.publish(Functions.identity()), false, 1, 1, 1); } @Test @@ -315,7 +259,7 @@ protected void subscribeActual(Subscriber s) { } } } - .publish(Functions.>identity(), 8) + .publish(Functions.identity(), 8) .test(0) .assertFailure(MissingBackpressureException.class); } @@ -323,12 +267,7 @@ protected void subscribeActual(Subscriber s) { @Test public void errorResubscribe() { Flowable.error(new TestException()) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable f) throws Exception { - return f.onErrorResumeWith(f); - } - }) + .publish(f -> f.onErrorResumeWith(f)) .test() .assertFailure(TestException.class); } @@ -336,21 +275,18 @@ public Publisher apply(Flowable f) throws Exception { @Test public void fusedInputCrash() { Flowable.just(1) - .map(new Function() { - @Override - public Integer apply(Integer v) throws Exception { - throw new TestException(); - } + .map(v -> { + throw new TestException(); }) - .publish(Functions.>identity()) + .publish(Functions.identity()) .test() .assertFailure(TestException.class); } @Test public void error() { - new FlowablePublishMulticast(Flowable.just(1).concatWith(Flowable.error(new TestException())), - Functions.>identity(), 16, true) + new FlowablePublishMulticast<>(Flowable.just(1).concatWith(Flowable.error(new TestException())), + Functions.identity(), 16, true) .test() .assertFailure(TestException.class, 1); } @@ -358,7 +294,7 @@ public void error() { @Test public void backpressuredEmpty() { Flowable.empty() - .publish(Functions.>identity()) + .publish(Functions.identity()) .test(0L) .assertResult(); } @@ -366,7 +302,7 @@ public void backpressuredEmpty() { @Test public void oneByOne() { Flowable.range(1, 10) - .publish(Functions.>identity()) + .publish(Functions.identity()) .rebatchRequests(1) .test() .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -387,7 +323,7 @@ public void onNext(Integer t) { } }; - pp.publish(Functions.>identity()).subscribe(ts); + pp.publish(Functions.identity()).subscribe(ts); pp.onNext(1); @@ -399,12 +335,7 @@ public void onNext(Integer t) { @Test public void inputOutputSubscribeRace() { Flowable source = Flowable.just(1) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(Flowable f) throws Exception { - return f.subscribeOn(Schedulers.single()); - } - }); + .publish(f -> f.subscribeOn(Schedulers.single())); for (int i = 0; i < 500; i++) { source.test() @@ -416,7 +347,7 @@ public Publisher apply(Flowable f) throws Exception { @Test public void inputOutputSubscribeRace2() { Flowable source = Flowable.just(1).subscribeOn(Schedulers.single()) - .publish(Functions.>identity()); + .publish(Functions.identity()); for (int i = 0; i < 500; i++) { source.test() @@ -428,33 +359,22 @@ public void inputOutputSubscribeRace2() { @Test public void sourceSubscriptionDelayed() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts1 = new TestSubscriber(0L); + final TestSubscriber ts1 = new TestSubscriber<>(0L); Flowable.just(1) - .publish(new Function, Publisher>() { - @Override - public Publisher apply(final Flowable f) throws Exception { - Runnable r1 = new Runnable() { - @Override - public void run() { - f.subscribe(ts1); - } - }; - - Runnable r2 = new Runnable() { - @Override - public void run() { + .publish(f -> { + Runnable r1 = () -> f.subscribe(ts1); + + Runnable r2 = () -> { for (int j = 0; j < 100; j++) { ts1.request(1); } - } - }; + }; - TestHelper.race(r1, r2); - return f; - } - }).test() - .assertResult(1); + TestHelper.race(r1, r2); + return f; + }).test() + .assertResult(1); ts1.assertResult(1); } @@ -463,25 +383,9 @@ public void run() { @Test public void longFlow() { Flowable.range(1, 1000000) - .publish(new Function, Publisher>() { - @SuppressWarnings("unchecked") - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.mergeArray( - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 == 0; - } - }), - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 != 0; - } - })); - } - }) + .publish(v -> Flowable.mergeArray( + v.filter(w -> w % 2 == 0), + v.filter(w -> w % 2 != 0))) .takeLast(1) .test() .assertResult(1000000); @@ -490,25 +394,9 @@ public boolean test(Integer w) throws Exception { @Test public void longFlow2() { Flowable.range(1, 100000) - .publish(new Function, Publisher>() { - @SuppressWarnings("unchecked") - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.mergeArray( - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 == 0; - } - }), - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 != 0; - } - })); - } - }) + .publish(v -> Flowable.mergeArray( + v.filter(w -> w % 2 == 0), + v.filter(w -> w % 2 != 0))) .test() .assertValueCount(100000) .assertNoErrors() @@ -518,27 +406,45 @@ public boolean test(Integer w) throws Exception { @Test public void longFlowHidden() { Flowable.range(1, 1000000).hide() - .publish(new Function, Publisher>() { - @SuppressWarnings("unchecked") - @Override - public Publisher apply(Flowable v) throws Exception { - return Flowable.mergeArray( - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 == 0; - } - }), - v.filter(new Predicate() { - @Override - public boolean test(Integer w) throws Exception { - return w % 2 != 0; - } - })); - } - }) + .publish(v -> Flowable.mergeArray( + v.filter(w -> w % 2 == 0), + v.filter(w -> w % 2 != 0))) .takeLast(1) .test() .assertResult(1000000); } + + @Test + public void noUpstreamCancelOnCasualChainClose() { + AtomicBoolean parentUpstreamCancelled = new AtomicBoolean(false); + Flowable.range(1, 10) + .doOnCancel(() -> parentUpstreamCancelled.set(true)) + .publish(Functions.identity()) + .test() + .awaitDone(1, TimeUnit.SECONDS); + assertFalse("Unnecessary upstream .cancel() call in FlowablePublishMulticast", parentUpstreamCancelled.get()); + } + + @Test + public void noUpstreamCancelOnCasualChainCloseWithInnerCancels() { + AtomicBoolean parentUpstreamCancelled = new AtomicBoolean(false); + Flowable.range(1, 10) + .doOnCancel(() -> parentUpstreamCancelled.set(true)) + .publish(v -> Flowable.concat(v.take(1), v.skip(5))) + .test() + .awaitDone(1, TimeUnit.SECONDS); + assertFalse("Unnecessary upstream .cancel() call in FlowablePublishMulticast", parentUpstreamCancelled.get()); + } + + @Test + public void upstreamCancelOnDownstreamCancel() { + AtomicBoolean parentUpstreamCancelled = new AtomicBoolean(false); + Flowable.range(1, 10) + .doOnCancel(() -> parentUpstreamCancelled.set(true)) + .publish(Functions.identity()) + .take(1) + .test() + .awaitDone(1, TimeUnit.SECONDS); + assertTrue("Upstream .cancel() not called in FlowablePublishMulticast", parentUpstreamCancelled.get()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticastTest.java index 186285e1f2..0e1bdbbdc0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishMulticastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,9 +20,9 @@ import org.junit.Test; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; import io.reactivex.rxjava3.internal.operators.flowable.FlowablePublishMulticast.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.UnicastProcessor; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -32,7 +32,7 @@ public class FlowablePublishMulticastTest extends RxJavaTest { @Test public void asyncFusedInput() { - MulticastProcessor mp = new MulticastProcessor(128, true); + MulticastProcessor mp = new MulticastProcessor<>(128, true); UnicastProcessor up = UnicastProcessor.create(); @@ -51,7 +51,7 @@ public void asyncFusedInput() { @Test public void fusionRejectedInput() { - MulticastProcessor mp = new MulticastProcessor(128, true); + MulticastProcessor mp = new MulticastProcessor<>(128, true); mp.onSubscribe(new QueueSubscription() { @@ -106,10 +106,10 @@ public void cancel() { public void addRemoveRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final MulticastProcessor mp = new MulticastProcessor(128, true); + final MulticastProcessor mp = new MulticastProcessor<>(128, true); - final MulticastSubscription ms1 = new MulticastSubscription(null, mp); - final MulticastSubscription ms2 = new MulticastSubscription(null, mp); + final MulticastSubscription ms1 = new MulticastSubscription<>(null, mp); + final MulticastSubscription ms2 = new MulticastSubscription<>(null, mp); assertTrue(mp.add(ms1)); @@ -133,9 +133,9 @@ public void run() { @Test public void removeNotFound() { - MulticastProcessor mp = new MulticastProcessor(128, true); + MulticastProcessor mp = new MulticastProcessor<>(128, true); - MulticastSubscription ms1 = new MulticastSubscription(null, mp); + MulticastSubscription ms1 = new MulticastSubscription<>(null, mp); assertTrue(mp.add(ms1)); mp.remove(null); @@ -143,9 +143,9 @@ public void removeNotFound() { @Test public void errorAllCancelled() { - MulticastProcessor mp = new MulticastProcessor(128, true); + MulticastProcessor mp = new MulticastProcessor<>(128, true); - MulticastSubscription ms1 = new MulticastSubscription(null, mp); + MulticastSubscription ms1 = new MulticastSubscription<>(null, mp); assertTrue(mp.add(ms1)); ms1.set(Long.MIN_VALUE); @@ -155,9 +155,9 @@ public void errorAllCancelled() { @Test public void completeAllCancelled() { - MulticastProcessor mp = new MulticastProcessor(128, true); + MulticastProcessor mp = new MulticastProcessor<>(128, true); - MulticastSubscription ms1 = new MulticastSubscription(null, mp); + MulticastSubscription ms1 = new MulticastSubscription<>(null, mp); assertTrue(mp.add(ms1)); ms1.set(Long.MIN_VALUE); @@ -167,9 +167,9 @@ public void completeAllCancelled() { @Test public void cancelledWhileFindingRequests() { - final MulticastProcessor mp = new MulticastProcessor(128, true); + final MulticastProcessor mp = new MulticastProcessor<>(128, true); - final MulticastSubscription ms1 = new MulticastSubscription(null, mp); + final MulticastSubscription ms1 = new MulticastSubscription<>(null, mp); assertTrue(mp.add(ms1)); @@ -182,9 +182,9 @@ public void cancelledWhileFindingRequests() { @Test public void negativeRequest() { - final MulticastProcessor mp = new MulticastProcessor(128, true); + final MulticastProcessor mp = new MulticastProcessor<>(128, true); - final MulticastSubscription ms1 = new MulticastSubscription(null, mp); + final MulticastSubscription ms1 = new MulticastSubscription<>(null, mp); List errors = TestHelper.trackPluginErrors(); try { @@ -198,19 +198,19 @@ public void negativeRequest() { @Test public void outputCancellerDoubleOnSubscribe() { - TestHelper.doubleOnSubscribe(new OutputCanceller(new TestSubscriber(), null)); + TestHelper.doubleOnSubscribe(new OutputCanceller<>(new TestSubscriber<>(), null)); } @Test public void dontDropItemsWhenNoReadyConsumers() { - final MulticastProcessor mp = new MulticastProcessor(128, true); + final MulticastProcessor mp = new MulticastProcessor<>(128, true); mp.onSubscribe(new BooleanSubscription()); mp.onNext(1); - TestSubscriber ts = new TestSubscriber(); - final MulticastSubscription ms1 = new MulticastSubscription(ts, mp); + TestSubscriber ts = new TestSubscriber<>(); + final MulticastSubscription ms1 = new MulticastSubscription<>(ts, mp); ts.onSubscribe(ms1); assertTrue(mp.add(ms1)); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java index 0ecba3926f..c3355e9a38 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowablePublishTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -128,7 +128,7 @@ public void run() { }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.merge(fast, slow).subscribe(ts); is.connect(); ts.awaitDone(5, TimeUnit.SECONDS); @@ -148,7 +148,7 @@ public void accept(Integer t1) { } }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); xs.publish(new Function, Flowable>() { @Override @@ -175,7 +175,7 @@ public boolean test(Integer i) { @Test public void takeUntilWithPublishedStream() { Flowable xs = Flowable.range(0, Flowable.bufferSize() * 2); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ConnectableFlowable xsp = xs.publish(); xsp.takeUntil(xsp.skipWhile(new Predicate() { @@ -211,7 +211,7 @@ public void run() { final AtomicBoolean child1Unsubscribed = new AtomicBoolean(); final AtomicBoolean child2Unsubscribed = new AtomicBoolean(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); final TestSubscriber ts1 = new TestSubscriber() { @Override @@ -259,7 +259,7 @@ public void connectWithNoSubscriber() { cf.connect(); // Emit 0 scheduler.advanceTimeBy(15, TimeUnit.MILLISECONDS); - TestSubscriber subscriber = new TestSubscriber(); + TestSubscriber subscriber = new TestSubscriber<>(); cf.subscribe(subscriber); // Emit 1 and 2 scheduler.advanceTimeBy(50, TimeUnit.MILLISECONDS); @@ -271,7 +271,7 @@ public void connectWithNoSubscriber() { public void subscribeAfterDisconnectThenConnect() { ConnectableFlowable source = Flowable.just(1).publish(); - TestSubscriberEx ts1 = new TestSubscriberEx(); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); source.subscribe(ts1); @@ -283,7 +283,7 @@ public void subscribeAfterDisconnectThenConnect() { source.reset(); - TestSubscriberEx ts2 = new TestSubscriberEx(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(); source.subscribe(ts2); @@ -301,7 +301,7 @@ public void subscribeAfterDisconnectThenConnect() { public void noSubscriberRetentionOnCompleted() { FlowablePublish source = (FlowablePublish)Flowable.just(1).publish(); - TestSubscriberEx ts1 = new TestSubscriberEx(); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); source.subscribe(ts1); @@ -353,7 +353,7 @@ static boolean checkPublishDisposed(Disposable d) { public void zeroRequested() { ConnectableFlowable source = Flowable.just(1).publish(); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); source.subscribe(ts); @@ -408,9 +408,9 @@ public void syncFusedObserveOn() { Flowable obs = cf.observeOn(Schedulers.computation()); for (int i = 0; i < 1000; i++) { for (int j = 1; j < 6; j++) { - List> tss = new ArrayList>(); + List> tss = new ArrayList<>(); for (int k = 1; k < j; k++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); tss.add(ts); obs.subscribe(ts); } @@ -435,9 +435,9 @@ public void syncFusedObserveOn2() { Flowable obs = cf.observeOn(ImmediateThinScheduler.INSTANCE); for (int i = 0; i < 1000; i++) { for (int j = 1; j < 6; j++) { - List> tss = new ArrayList>(); + List> tss = new ArrayList<>(); for (int k = 1; k < j; k++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); tss.add(ts); obs.subscribe(ts); } @@ -461,9 +461,9 @@ public void asyncFusedObserveOn() { ConnectableFlowable cf = Flowable.range(0, 1000).observeOn(ImmediateThinScheduler.INSTANCE).publish(); for (int i = 0; i < 1000; i++) { for (int j = 1; j < 6; j++) { - List> tss = new ArrayList>(); + List> tss = new ArrayList<>(); for (int k = 1; k < j; k++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); tss.add(ts); cf.subscribe(ts); } @@ -488,9 +488,9 @@ public void observeOn() { Flowable obs = cf.observeOn(Schedulers.computation()); for (int i = 0; i < 1000; i++) { for (int j = 1; j < 6; j++) { - List> tss = new ArrayList>(); + List> tss = new ArrayList<>(); for (int k = 1; k < j; k++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); tss.add(ts); obs.subscribe(ts); } @@ -539,7 +539,7 @@ public void addRemoveRace() { final TestSubscriber ts = cf.test(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -694,7 +694,7 @@ public void subscribeDisconnectRace() { final ConnectableFlowable cf = pp.publish(); final Disposable d = cf.connect(); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -905,9 +905,9 @@ public void subscribe(FlowableEmitter s) throws Exception { .test(0L) // 3.x emits errors last, even the full queue errors .requestMore(10) - .assertFailure(MissingBackpressureException.class, 0, 1, 2, 3, 4, 5, 6, 7); + .assertFailure(QueueOverflowException.class, 0, 1, 2, 3, 4, 5, 6, 7); - TestHelper.assertError(errors, 0, MissingBackpressureException.class); + TestHelper.assertError(errors, 0, QueueOverflowException.class); } finally { RxJavaPlugins.reset(); } @@ -938,7 +938,7 @@ protected void subscribeActual(Subscriber s) { public void disposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference ref = new AtomicReference(); + final AtomicReference ref = new AtomicReference<>(); final ConnectableFlowable cf = new Flowable() { @Override @@ -963,7 +963,7 @@ public void run() { @Test public void removeNotPresent() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); final ConnectableFlowable cf = new Flowable() { @Override @@ -976,7 +976,7 @@ protected void subscribeActual(Subscriber s) { cf.connect(); - ref.get().add(new InnerSubscription(new TestSubscriber(), ref.get())); + ref.get().add(new InnerSubscription<>(new TestSubscriber<>(), ref.get())); ref.get().remove(null); } @@ -999,7 +999,7 @@ public void onNext(Integer t) { ts1.assertResult(1); - TestSubscriber ts2 = new TestSubscriber(0); + TestSubscriber ts2 = new TestSubscriber<>(0); cf.subscribe(ts2); ts2 @@ -1012,7 +1012,7 @@ public void onNext(Integer t) { public void subscriberLiveSwap() { final ConnectableFlowable cf = Flowable.range(1, 5).publish(); - final TestSubscriber ts2 = new TestSubscriber(0); + final TestSubscriber ts2 = new TestSubscriber<>(0); TestSubscriber ts1 = new TestSubscriber() { @Override @@ -1038,7 +1038,7 @@ public void onNext(Integer t) { @Test public void selectorSubscriberSwap() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); Flowable.range(1, 5).publish(new Function, Publisher>() { @Override @@ -1061,7 +1061,7 @@ public Publisher apply(Flowable f) throws Exception { @Test public void leavingSubscriberOverrequests() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); PublishProcessor pp = PublishProcessor.create(); @@ -1205,7 +1205,7 @@ public Publisher apply(Integer first) @Test public void publishFunctionCancelOuterAfterOneInner() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); PublishProcessor pp = PublishProcessor.create(); @@ -1231,7 +1231,7 @@ public void onNext(Integer t) { @Test public void publishFunctionCancelOuterAfterOneInnerBackpressured() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); PublishProcessor pp = PublishProcessor.create(); @@ -1261,7 +1261,7 @@ public void publishCancelOneAsync() { final PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); pp.publish(new Function, Publisher>() { @Override @@ -1300,9 +1300,9 @@ public void publishCancelOneAsync2() { ConnectableFlowable cf = pp.publish(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); cf.subscribe(new FlowableSubscriber() { @SuppressWarnings("unchecked") @@ -1368,7 +1368,6 @@ public void badRequest() { } @Test - @SuppressWarnings("unchecked") public void splitCombineSubscriberChangeAfterOnNext() { Flowable source = Flowable.range(0, 20) .doOnSubscribe(new Consumer() { @@ -1436,7 +1435,6 @@ public boolean test(List v) throws Exception { } @Test - @SuppressWarnings("unchecked") public void splitCombineSubscriberChangeAfterOnNextFused() { Flowable source = Flowable.range(0, 20) .publish(10) @@ -1500,7 +1498,7 @@ public boolean test(List v) throws Exception { @Test public void altConnectCrash() { try { - new FlowablePublish(Flowable.empty(), 128) + new FlowablePublish<>(Flowable.empty(), 128) .connect(new Consumer() { @Override public void accept(Disposable t) throws Exception { @@ -1517,7 +1515,7 @@ public void accept(Disposable t) throws Exception { public void altConnectRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { final ConnectableFlowable cf = - new FlowablePublish(Flowable.never(), 128); + new FlowablePublish<>(Flowable.never(), 128); Runnable r = new Runnable() { @Override @@ -1598,7 +1596,7 @@ protected void subscribeActual(Subscriber s) { .refCount() .test(0) .requestMore(1) - .assertFailure(MissingBackpressureException.class, 1); + .assertFailure(QueueOverflowException.class, 1); } @Test @@ -1699,4 +1697,110 @@ public void disposeResets() { ts.assertValuesOnly(1); } + + @Test(expected = TestException.class) + public void connectDisposeCrash() { + ConnectableFlowable cf = Flowable.never().publish(); + + cf.connect(); + + cf.connect(d -> { throw new TestException(); }); + } + + @Test + public void resetWhileNotConnectedIsNoOp() { + ConnectableFlowable cf = Flowable.never().publish(); + + cf.reset(); + } + + @Test + public void resetWhileActiveIsNoOp() { + ConnectableFlowable cf = Flowable.never().publish(); + + cf.connect(); + + cf.reset(); + } + + @Test + public void crossCancelOnComplete() { + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber() { + @Override + public void onComplete() { + super.onComplete(); + ts1.cancel(); + } + }; + + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.publish(); + + cf.subscribe(ts2); + cf.subscribe(ts1); + + cf.connect(); + + pp.onComplete(); + + ts2.assertResult(); + + ts1.assertEmpty(); + } + + @Test + public void crossCancelOnError() { + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber() { + @Override + public void onError(Throwable t) { + super.onError(t); + ts1.cancel(); + } + }; + + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.publish(); + + cf.subscribe(ts2); + cf.subscribe(ts1); + + cf.connect(); + + pp.onError(new TestException()); + + ts2.assertFailure(TestException.class); + + ts1.assertEmpty(); + } + + @Test + public void disposeNoNeedForReset() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.publish(); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java index afc677aefe..bc24ab787c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeLongTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.*; @@ -98,7 +98,7 @@ public void rangeWithOverflow5() { public void backpressureViaRequest() { Flowable f = Flowable.rangeLong(1, Flowable.bufferSize()); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.assertNoValues(); ts.request(1); @@ -119,14 +119,14 @@ public void backpressureViaRequest() { @Test public void noBackpressure() { - ArrayList list = new ArrayList(Flowable.bufferSize() * 2); + ArrayList list = new ArrayList<>(Flowable.bufferSize() * 2); for (long i = 1; i <= Flowable.bufferSize() * 2 + 1; i++) { list.add(i); } Flowable f = Flowable.rangeLong(1, list.size()); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.assertNoValues(); ts.request(Long.MAX_VALUE); // infinite @@ -139,11 +139,11 @@ public void noBackpressure() { void withBackpressureOneByOne(long start) { Flowable source = Flowable.rangeLong(start, 100); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.request(1); source.subscribe(ts); - List list = new ArrayList(100); + List list = new ArrayList<>(100); for (long i = 0; i < 100; i++) { list.add(i + start); ts.request(1); @@ -154,11 +154,11 @@ void withBackpressureOneByOne(long start) { void withBackpressureAllAtOnce(long start) { Flowable source = Flowable.rangeLong(start, 100); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.request(100); source.subscribe(ts); - List list = new ArrayList(100); + List list = new ArrayList<>(100); for (long i = 0; i < 100; i++) { list.add(i + start); } @@ -184,11 +184,11 @@ public void withBackpressureAllAtOnce() { public void withBackpressureRequestWayMore() { Flowable source = Flowable.rangeLong(50, 100); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.request(150); source.subscribe(ts); - List list = new ArrayList(100); + List list = new ArrayList<>(100); for (long i = 0; i < 100; i++) { list.add(i + 50); } @@ -257,7 +257,7 @@ public void onNext(Long t) { @Test public void nearMaxValueWithoutBackpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.rangeLong(Long.MAX_VALUE - 1L, 2L).subscribe(ts); ts.assertComplete(); @@ -267,7 +267,7 @@ public void nearMaxValueWithoutBackpressure() { @Test public void nearMaxValueWithBackpressure() { - TestSubscriber ts = new TestSubscriber(3L); + TestSubscriber ts = new TestSubscriber<>(3L); Flowable.rangeLong(Long.MAX_VALUE - 1L, 2L).subscribe(ts); ts.assertComplete(); @@ -539,4 +539,44 @@ public boolean test(Long v) throws Exception { ts.assertResult(2L, 4L); } + + @Test + public void slowPathCancelBeforeComplete() { + Flowable.rangeLong(1, 2) + .take(2) + .test() + .assertResult(1L, 2L); + } + + @Test + public void conditionalFastPathCancelBeforeComplete() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.rangeLong(1, 2) + .compose(TestHelper.conditional()) + .doOnNext(v -> { + if (v == 2L) { + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1L, 2L); + } + + @Test + public void conditionalSlowPathTake() { + TestSubscriber ts = new TestSubscriber<>(4); + + Flowable.rangeLong(1, 3) + .compose(TestHelper.conditional()) + .doOnNext(v -> { + if (v == 2L) { + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1L, 2L); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java index a8d42213c9..53264bddc0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRangeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.*; @@ -98,7 +98,7 @@ public void rangeWithOverflow5() { public void backpressureViaRequest() { Flowable f = Flowable.range(1, Flowable.bufferSize()); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.assertNoValues(); ts.request(1); @@ -119,14 +119,14 @@ public void backpressureViaRequest() { @Test public void noBackpressure() { - ArrayList list = new ArrayList(Flowable.bufferSize() * 2); + ArrayList list = new ArrayList<>(Flowable.bufferSize() * 2); for (int i = 1; i <= Flowable.bufferSize() * 2 + 1; i++) { list.add(i); } Flowable f = Flowable.range(1, list.size()); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.assertNoValues(); ts.request(Long.MAX_VALUE); // infinite @@ -139,11 +139,11 @@ public void noBackpressure() { void withBackpressureOneByOne(int start) { Flowable source = Flowable.range(start, 100); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.request(1); source.subscribe(ts); - List list = new ArrayList(100); + List list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { list.add(i + start); ts.request(1); @@ -154,11 +154,11 @@ void withBackpressureOneByOne(int start) { void withBackpressureAllAtOnce(int start) { Flowable source = Flowable.range(start, 100); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.request(100); source.subscribe(ts); - List list = new ArrayList(100); + List list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { list.add(i + start); } @@ -184,11 +184,11 @@ public void withBackpressureAllAtOnce() { public void withBackpressureRequestWayMore() { Flowable source = Flowable.range(50, 100); - TestSubscriberEx ts = new TestSubscriberEx(0L); + TestSubscriberEx ts = new TestSubscriberEx<>(0L); ts.request(150); source.subscribe(ts); - List list = new ArrayList(100); + List list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { list.add(i + 50); } @@ -257,7 +257,7 @@ public void onNext(Integer t) { @Test public void nearMaxValueWithoutBackpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(Integer.MAX_VALUE - 1, 2).subscribe(ts); ts.assertComplete(); @@ -267,7 +267,7 @@ public void nearMaxValueWithoutBackpressure() { @Test public void nearMaxValueWithBackpressure() { - TestSubscriber ts = new TestSubscriber(3L); + TestSubscriber ts = new TestSubscriber<>(3L); Flowable.range(Integer.MAX_VALUE - 1, 2).subscribe(ts); ts.assertComplete(); @@ -590,4 +590,28 @@ public void onNext(Integer t) { ts.assertResult(1, 2); } + + @Test + public void slowPathCancelBeforeComplete() { + Flowable.range(1, 2) + .take(2) + .test() + .assertResult(1, 2); + } + + @Test + public void conditionalFastPatchCancelBeforeComplete() { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.range(1, 2) + .compose(TestHelper.conditional()) + .doOnNext(v -> { + if (v == 2) { + ts.cancel(); + } + }) + .subscribe(ts); + + ts.assertValuesOnly(1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java index bc7d1e8ece..16b491a352 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -530,4 +530,9 @@ public Integer apply(Integer a, Integer b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribeFlowable() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.reduce((a, b) -> a).toFlowable()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingleTest.java index d3da48ffe5..3de991aa2e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReduceWithSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java index c70429b381..48fbb633ae 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRefCountTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,25 @@ public class FlowableRefCountTest extends RxJavaTest { @Test - public void refCountAsync() { + public void refCountAsync() throws InterruptedException { + // Flaky + for (int i = 0; i < 10; i++) { + try { + refCountAsyncActual(); + return; + } catch (AssertionError ex) { + if (i == 9) { + throw ex; + } + Thread.sleep((int)(200 * (Math.random() * 10 + 1))); + } + } + } + + /** + * Tries to coordinate async counting but it is flaky due to the low 10s of milliseconds. + */ + void refCountAsyncActual() { final AtomicInteger subscribeCount = new AtomicInteger(); final AtomicInteger nextCount = new AtomicInteger(); Flowable r = Flowable.interval(0, 20, TimeUnit.MILLISECONDS) @@ -205,8 +223,8 @@ public void run() { .publish().refCount(); for (int i = 0; i < 10; i++) { - TestSubscriber ts1 = new TestSubscriber(); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber<>(); r.subscribe(ts1); r.subscribe(ts2); try { @@ -248,7 +266,7 @@ public void run() { } }); - TestSubscriberEx s = new TestSubscriberEx(); + TestSubscriberEx s = new TestSubscriberEx<>(); f.publish().refCount().subscribeOn(Schedulers.newThread()).subscribe(s); System.out.println("send unsubscribe"); // wait until connected @@ -293,7 +311,7 @@ public void accept(Subscription s) { } }); - TestSubscriberEx s = new TestSubscriberEx(); + TestSubscriberEx s = new TestSubscriberEx<>(); f.publish().refCount().subscribeOn(Schedulers.computation()).subscribe(s); System.out.println("send unsubscribe"); @@ -386,7 +404,7 @@ public void refCount() { Flowable interval = Flowable.interval(100, TimeUnit.MILLISECONDS, s).publish().refCount(); // subscribe list1 - final List list1 = new ArrayList(); + final List list1 = new ArrayList<>(); Disposable d1 = interval.subscribe(new Consumer() { @Override public void accept(Long t1) { @@ -401,7 +419,7 @@ public void accept(Long t1) { assertEquals(1L, list1.get(1).longValue()); // subscribe list2 - final List list2 = new ArrayList(); + final List list2 = new ArrayList<>(); Disposable d2 = interval.subscribe(new Consumer() { @Override public void accept(Long t1) { @@ -446,7 +464,7 @@ public void accept(Long t1) { // subscribing a new one should start over because the source should have been unsubscribed // subscribe list3 - final List list3 = new ArrayList(); + final List list3 = new ArrayList<>(); interval.subscribe(new Consumer() { @Override public void accept(Long t1) { @@ -517,8 +535,8 @@ public Integer apply(Integer t1, Integer t2) { }) .publish().refCount(); - TestSubscriberEx ts1 = new TestSubscriberEx(); - TestSubscriberEx ts2 = new TestSubscriberEx(); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(); combined.subscribe(ts1); combined.subscribe(ts2); @@ -805,7 +823,7 @@ static final class BadFlowableSubscribe extends ConnectableFlowable { @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -827,7 +845,7 @@ static final class BadFlowableDispose extends ConnectableFlowable { @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -922,7 +940,7 @@ static final class BadFlowableSubscribe2 extends ConnectableFlowable { @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -969,7 +987,7 @@ static final class BadFlowableConnect2 extends ConnectableFlowable { @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -1184,7 +1202,7 @@ public void unsubscribeSubscribeRace() { final TestSubscriber ts1 = source.test(0); - final TestSubscriber ts2 = new TestSubscriber(0); + final TestSubscriber ts2 = new TestSubscriber<>(0); Runnable r1 = new Runnable() { @Override @@ -1214,7 +1232,7 @@ static final class BadFlowableDoubleOnX extends ConnectableFlowable @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -1388,11 +1406,11 @@ protected void subscribeActual(Subscriber subscriber) { @Test public void timeoutResetsSource() { - TestConnectableFlowable tcf = new TestConnectableFlowable(); + TestConnectableFlowable tcf = new TestConnectableFlowable<>(); FlowableRefCount o = (FlowableRefCount)tcf.refCount(); RefConnection rc = new RefConnection(o); - rc.set(Disposables.empty()); + rc.set(Disposable.empty()); o.connection = rc; o.timeout(rc); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatTest.java index c99e09a45a..9086c32a87 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRepeatTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -164,7 +164,7 @@ public void repeatAndDistinctUnbounded() { .repeat(3) .distinct(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); src.subscribe(ts); @@ -176,8 +176,8 @@ public void repeatAndDistinctUnbounded() { /** Issue #2844: wrong target of request. */ @Test public void repeatRetarget() { - final List concatBase = new ArrayList(); - TestSubscriber ts = new TestSubscriber(); + final List concatBase = new ArrayList<>(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2) .repeat(5) .concatMap(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java index d17526e47b..d2a4ba56da 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayEagerTruncateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -732,14 +732,21 @@ public boolean isDisposed() { @Test public void boundedReplayBuffer() { - BoundedReplayBuffer buf = new BoundedReplayBuffer(true); + BoundedReplayBuffer buf = new BoundedReplayBuffer(true) { + private static final long serialVersionUID = -9081211580719235896L; + + @Override + void truncate() { + } + }; + buf.addLast(new Node(1, 0)); buf.addLast(new Node(2, 1)); buf.addLast(new Node(3, 2)); buf.addLast(new Node(4, 3)); buf.addLast(new Node(5, 4)); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.collect(values); Assert.assertEquals(Arrays.asList(1, 2, 3, 4, 5), values); @@ -762,8 +769,8 @@ public void boundedReplayBuffer() { @Test public void timedAndSizedTruncation() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, true); - List values = new ArrayList(); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, true); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -808,8 +815,8 @@ public void accept(long t) { }); ConnectableFlowable cf = source.replay(); - TestSubscriberEx ts1 = new TestSubscriberEx(10L); - TestSubscriberEx ts2 = new TestSubscriberEx(90L); + TestSubscriberEx ts1 = new TestSubscriberEx<>(10L); + TestSubscriberEx ts2 = new TestSubscriberEx<>(90L); cf.subscribe(ts1); cf.subscribe(ts2); @@ -839,8 +846,8 @@ public void accept(long t) { }); ConnectableFlowable cf = source.replay(50, true); - TestSubscriberEx ts1 = new TestSubscriberEx(10L); - TestSubscriberEx ts2 = new TestSubscriberEx(90L); + TestSubscriberEx ts1 = new TestSubscriberEx<>(10L); + TestSubscriberEx ts2 = new TestSubscriberEx<>(90L); cf.subscribe(ts1); cf.subscribe(ts2); @@ -862,7 +869,7 @@ public void accept(long t) { public void coldReplayNoBackpressure() { Flowable source = Flowable.range(0, 1000).replay().autoConnect(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.subscribe(ts); @@ -880,7 +887,7 @@ public void coldReplayNoBackpressure() { public void coldReplayBackpressure() { Flowable source = Flowable.range(0, 1000).replay().autoConnect(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(10); source.subscribe(ts); @@ -961,7 +968,7 @@ public void unsubscribeSource() throws Throwable { @Test public void take() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable cached = Flowable.range(1, 100).replay().autoConnect(); cached.take(10).subscribe(ts); @@ -975,7 +982,7 @@ public void take() { public void async() { Flowable source = Flowable.range(1, 10000); for (int i = 0; i < 100; i++) { - TestSubscriberEx ts1 = new TestSubscriberEx(); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); Flowable cached = source.replay().autoConnect(); @@ -986,7 +993,7 @@ public void async() { ts1.assertTerminated(); assertEquals(10000, ts1.values().size()); - TestSubscriberEx ts2 = new TestSubscriberEx(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(); cached.observeOn(Schedulers.computation()).subscribe(ts2); ts2.awaitDone(2, TimeUnit.SECONDS); @@ -1005,14 +1012,14 @@ public void asyncComeAndGo() { Flowable output = cached.observeOn(Schedulers.computation(), false, 1024); - List> list = new ArrayList>(100); + List> list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); list.add(ts); output.skip(i * 10).take(10).subscribe(ts); } - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 10; i++) { expected.add((long)(i - 10)); } @@ -1046,7 +1053,7 @@ public void subscribe(Subscriber t) { } }); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); firehose.replay().autoConnect().observeOn(Schedulers.computation()).takeLast(100).subscribe(ts); ts.awaitDone(3, TimeUnit.SECONDS); @@ -1062,14 +1069,14 @@ public void valuesAndThenError() { .concatWith(Flowable.error(new TestException())) .replay().autoConnect(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.subscribe(ts); ts.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); ts.assertNotComplete(); Assert.assertEquals(1, ts.errors().size()); - TestSubscriberEx ts2 = new TestSubscriberEx(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(); source.subscribe(ts2); ts2.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -1110,7 +1117,7 @@ public void onNext(Integer t) { public void unboundedLeavesEarly() { PublishProcessor source = PublishProcessor.create(); - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable out = source .doOnRequest(new LongConsumer() { @@ -1120,8 +1127,8 @@ public void accept(long t) { } }).replay().autoConnect(); - TestSubscriber ts1 = new TestSubscriber(5L); - TestSubscriber ts2 = new TestSubscriber(10L); + TestSubscriber ts1 = new TestSubscriber<>(5L); + TestSubscriber ts2 = new TestSubscriber<>(10L); out.subscribe(ts1); out.subscribe(ts2); @@ -1135,7 +1142,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ConnectableFlowable source = Flowable.range(1, 10).replay(1, true); source.connect(); - TestSubscriber ts1 = new TestSubscriber(2L); + TestSubscriber ts1 = new TestSubscriber<>(2L); source.subscribe(ts1); @@ -1143,7 +1150,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts1.assertNoErrors(); ts1.cancel(); - TestSubscriber ts2 = new TestSubscriber(2L); + TestSubscriber ts2 = new TestSubscriber<>(2L); source.subscribe(ts2); @@ -1151,7 +1158,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts2.assertNoErrors(); ts2.cancel(); - TestSubscriber ts21 = new TestSubscriber(1L); + TestSubscriber ts21 = new TestSubscriber<>(1L); source.subscribe(ts21); @@ -1159,7 +1166,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts21.assertNoErrors(); ts21.cancel(); - TestSubscriber ts22 = new TestSubscriber(1L); + TestSubscriber ts22 = new TestSubscriber<>(1L); source.subscribe(ts22); @@ -1167,7 +1174,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts22.assertNoErrors(); ts22.cancel(); - TestSubscriber ts3 = new TestSubscriber(); + TestSubscriber ts3 = new TestSubscriber<>(); source.subscribe(ts3); @@ -1182,7 +1189,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ConnectableFlowable source = Flowable.range(1, 10).replay(2, true); source.connect(); - TestSubscriber ts1 = new TestSubscriber(2L); + TestSubscriber ts1 = new TestSubscriber<>(2L); source.subscribe(ts1); @@ -1190,7 +1197,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts1.assertNoErrors(); ts1.cancel(); - TestSubscriber ts11 = new TestSubscriber(2L); + TestSubscriber ts11 = new TestSubscriber<>(2L); source.subscribe(ts11); @@ -1198,7 +1205,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts11.assertNoErrors(); ts11.cancel(); - TestSubscriber ts2 = new TestSubscriber(3L); + TestSubscriber ts2 = new TestSubscriber<>(3L); source.subscribe(ts2); @@ -1206,7 +1213,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts2.assertNoErrors(); ts2.cancel(); - TestSubscriber ts21 = new TestSubscriber(1L); + TestSubscriber ts21 = new TestSubscriber<>(1L); source.subscribe(ts21); @@ -1214,7 +1221,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts21.assertNoErrors(); ts21.cancel(); - TestSubscriber ts22 = new TestSubscriber(1L); + TestSubscriber ts22 = new TestSubscriber<>(1L); source.subscribe(ts22); @@ -1222,7 +1229,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts22.assertNoErrors(); ts22.cancel(); - TestSubscriber ts3 = new TestSubscriber(); + TestSubscriber ts3 = new TestSubscriber<>(); source.subscribe(ts3); @@ -1286,8 +1293,8 @@ public void subscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableFlowable cf = Flowable.range(1, 3).replay(); - final TestSubscriber ts1 = new TestSubscriber(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); + final TestSubscriber ts2 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -1312,8 +1319,8 @@ public void addRemoveRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableFlowable cf = Flowable.range(1, 3).replay(); - final TestSubscriber ts1 = new TestSubscriber(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); + final TestSubscriber ts2 = new TestSubscriber<>(); cf.subscribe(ts1); @@ -1412,7 +1419,7 @@ public void subscribeOnNextRace() { final ConnectableFlowable cf = pp.replay(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -1441,7 +1448,7 @@ public void unsubscribeOnNextRace() { final ConnectableFlowable cf = pp.replay(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); cf.subscribe(ts1); @@ -1470,7 +1477,7 @@ public void unsubscribeReplayRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableFlowable cf = Flowable.range(1, 1000).replay(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); cf.connect(); @@ -1592,12 +1599,12 @@ public void replayMaxInt() { @Test public void timedAndSizedTruncationError() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, true); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, true); Assert.assertFalse(buf.hasCompleted()); Assert.assertFalse(buf.hasError()); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -1635,8 +1642,8 @@ public void timedAndSizedTruncationError() { @Test public void sizedTruncation() { - SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer(2, true); - List values = new ArrayList(); + SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer<>(2, true); + List values = new ArrayList<>(); buf.next(1); buf.next(2); @@ -1916,19 +1923,6 @@ public ReplayBuffer get() throws Exception { .assertFailure(TestException.class); } - @Test - public void currentDisposedWhenConnecting() { - FlowableReplay fr = (FlowableReplay)FlowableReplay.create(Flowable.never(), 16, true); - fr.connect(); - - fr.current.get().dispose(); - assertTrue(fr.current.get().isDisposed()); - - fr.connect(); - - assertFalse(fr.current.get().isDisposed()); - } - @Test public void noBoundedRetentionViaThreadLocal() throws Exception { Flowable source = Flowable.range(1, 200) @@ -2268,4 +2262,85 @@ public void timeAndSizeNoTerminalTruncationOnTimechange() { .assertComplete() .assertNoErrors(); } + + @Test + public void disposeNoNeedForResetSizeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, true); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeAndSIzeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, 10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java index b8a7ba3e9d..b644629a6d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableReplayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,6 +17,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.io.IOException; import java.lang.management.*; import java.util.*; import java.util.concurrent.*; @@ -37,6 +38,7 @@ import io.reactivex.rxjava3.internal.fuseable.HasUpstreamPublisher; import io.reactivex.rxjava3.internal.operators.flowable.FlowableReplay.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.util.BackpressureHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.*; @@ -44,6 +46,7 @@ import io.reactivex.rxjava3.testsupport.*; public class FlowableReplayTest extends RxJavaTest { + @Test public void bufferedReplay() { PublishProcessor source = PublishProcessor.create(); @@ -732,14 +735,21 @@ public boolean isDisposed() { @Test public void boundedReplayBuffer() { - BoundedReplayBuffer buf = new BoundedReplayBuffer(false); + BoundedReplayBuffer buf = new BoundedReplayBuffer(false) { + private static final long serialVersionUID = -9081211580719235896L; + + @Override + void truncate() { + } + }; + buf.addLast(new Node(1, 0)); buf.addLast(new Node(2, 1)); buf.addLast(new Node(3, 2)); buf.addLast(new Node(4, 3)); buf.addLast(new Node(5, 4)); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.collect(values); Assert.assertEquals(Arrays.asList(1, 2, 3, 4, 5), values); @@ -760,11 +770,24 @@ public void boundedReplayBuffer() { } + @Test(expected = IllegalStateException.class) + public void boundedRemoveFirstOneItemOnly() { + BoundedReplayBuffer buf = new BoundedReplayBuffer(false) { + private static final long serialVersionUID = -9081211580719235896L; + + @Override + void truncate() { + } + }; + + buf.removeFirst(); + } + @Test public void timedAndSizedTruncation() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, false); - List values = new ArrayList(); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, false); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -809,8 +832,8 @@ public void accept(long t) { }); ConnectableFlowable cf = source.replay(); - TestSubscriberEx ts1 = new TestSubscriberEx(10L); - TestSubscriberEx ts2 = new TestSubscriberEx(90L); + TestSubscriberEx ts1 = new TestSubscriberEx<>(10L); + TestSubscriberEx ts2 = new TestSubscriberEx<>(90L); cf.subscribe(ts1); cf.subscribe(ts2); @@ -840,8 +863,8 @@ public void accept(long t) { }); ConnectableFlowable cf = source.replay(50); - TestSubscriberEx ts1 = new TestSubscriberEx(10L); - TestSubscriberEx ts2 = new TestSubscriberEx(90L); + TestSubscriberEx ts1 = new TestSubscriberEx<>(10L); + TestSubscriberEx ts2 = new TestSubscriberEx<>(90L); cf.subscribe(ts1); cf.subscribe(ts2); @@ -863,7 +886,7 @@ public void accept(long t) { public void coldReplayNoBackpressure() { Flowable source = Flowable.range(0, 1000).replay().autoConnect(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.subscribe(ts); @@ -881,7 +904,7 @@ public void coldReplayNoBackpressure() { public void coldReplayBackpressure() { Flowable source = Flowable.range(0, 1000).replay().autoConnect(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); ts.request(10); source.subscribe(ts); @@ -962,10 +985,12 @@ public void unsubscribeSource() throws Throwable { @Test public void take() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable cached = Flowable.range(1, 100).replay().autoConnect(); - cached.take(10).subscribe(ts); + cached + .take(10) + .subscribe(ts); ts.assertNoErrors(); ts.assertTerminated(); @@ -976,7 +1001,7 @@ public void take() { public void async() { Flowable source = Flowable.range(1, 10000); for (int i = 0; i < 100; i++) { - TestSubscriberEx ts1 = new TestSubscriberEx(); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); Flowable cached = source.replay().autoConnect(); @@ -987,7 +1012,7 @@ public void async() { ts1.assertTerminated(); assertEquals(10000, ts1.values().size()); - TestSubscriberEx ts2 = new TestSubscriberEx(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(); cached.observeOn(Schedulers.computation()).subscribe(ts2); ts2.awaitDone(2, TimeUnit.SECONDS); @@ -1006,14 +1031,14 @@ public void asyncComeAndGo() { Flowable output = cached.observeOn(Schedulers.computation(), false, 1024); - List> list = new ArrayList>(100); + List> list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); list.add(ts); output.skip(i * 10).take(10).subscribe(ts); } - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 10; i++) { expected.add((long)(i - 10)); } @@ -1047,7 +1072,7 @@ public void subscribe(Subscriber t) { } }); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); firehose.replay().autoConnect().observeOn(Schedulers.computation()).takeLast(100).subscribe(ts); ts.awaitDone(3, TimeUnit.SECONDS); @@ -1063,14 +1088,14 @@ public void valuesAndThenError() { .concatWith(Flowable.error(new TestException())) .replay().autoConnect(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.subscribe(ts); ts.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); ts.assertNotComplete(); Assert.assertEquals(1, ts.errors().size()); - TestSubscriberEx ts2 = new TestSubscriberEx(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(); source.subscribe(ts2); ts2.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -1079,7 +1104,7 @@ public void valuesAndThenError() { } @Test - public void unsafeChildThrows() { + public void unsafeChildOnNextThrows() { final AtomicInteger count = new AtomicInteger(); Flowable source = Flowable.range(1, 100) @@ -1107,11 +1132,57 @@ public void onNext(Integer t) { ts.assertError(TestException.class); } + @Test + public void unsafeChildOnErrorThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.error(new IOException()) + .replay() + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onError(Throwable t) { + super.onError(t); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertFailure(IOException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void unsafeChildOnCompleteThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.empty() + .replay() + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onComplete() { + super.onComplete(); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertResult(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + @Test public void unboundedLeavesEarly() { PublishProcessor source = PublishProcessor.create(); - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable out = source .doOnRequest(new LongConsumer() { @@ -1121,8 +1192,8 @@ public void accept(long t) { } }).replay().autoConnect(); - TestSubscriber ts1 = new TestSubscriber(5L); - TestSubscriber ts2 = new TestSubscriber(10L); + TestSubscriber ts1 = new TestSubscriber<>(5L); + TestSubscriber ts2 = new TestSubscriber<>(10L); out.subscribe(ts1); out.subscribe(ts2); @@ -1136,7 +1207,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ConnectableFlowable source = Flowable.range(1, 10).replay(1); source.connect(); - TestSubscriber ts1 = new TestSubscriber(2L); + TestSubscriber ts1 = new TestSubscriber<>(2L); source.subscribe(ts1); @@ -1144,7 +1215,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts1.assertNoErrors(); ts1.cancel(); - TestSubscriber ts2 = new TestSubscriber(2L); + TestSubscriber ts2 = new TestSubscriber<>(2L); source.subscribe(ts2); @@ -1152,7 +1223,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts2.assertNoErrors(); ts2.cancel(); - TestSubscriber ts21 = new TestSubscriber(1L); + TestSubscriber ts21 = new TestSubscriber<>(1L); source.subscribe(ts21); @@ -1160,7 +1231,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts21.assertNoErrors(); ts21.cancel(); - TestSubscriber ts22 = new TestSubscriber(1L); + TestSubscriber ts22 = new TestSubscriber<>(1L); source.subscribe(ts22); @@ -1168,7 +1239,7 @@ public void subscribersComeAndGoAtRequestBoundaries() { ts22.assertNoErrors(); ts22.cancel(); - TestSubscriber ts3 = new TestSubscriber(); + TestSubscriber ts3 = new TestSubscriber<>(); source.subscribe(ts3); @@ -1183,7 +1254,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ConnectableFlowable source = Flowable.range(1, 10).replay(2); source.connect(); - TestSubscriber ts1 = new TestSubscriber(2L); + TestSubscriber ts1 = new TestSubscriber<>(2L); source.subscribe(ts1); @@ -1191,7 +1262,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts1.assertNoErrors(); ts1.cancel(); - TestSubscriber ts11 = new TestSubscriber(2L); + TestSubscriber ts11 = new TestSubscriber<>(2L); source.subscribe(ts11); @@ -1199,7 +1270,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts11.assertNoErrors(); ts11.cancel(); - TestSubscriber ts2 = new TestSubscriber(3L); + TestSubscriber ts2 = new TestSubscriber<>(3L); source.subscribe(ts2); @@ -1207,7 +1278,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts2.assertNoErrors(); ts2.cancel(); - TestSubscriber ts21 = new TestSubscriber(1L); + TestSubscriber ts21 = new TestSubscriber<>(1L); source.subscribe(ts21); @@ -1215,7 +1286,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts21.assertNoErrors(); ts21.cancel(); - TestSubscriber ts22 = new TestSubscriber(1L); + TestSubscriber ts22 = new TestSubscriber<>(1L); source.subscribe(ts22); @@ -1223,7 +1294,7 @@ public void subscribersComeAndGoAtRequestBoundaries2() { ts22.assertNoErrors(); ts22.cancel(); - TestSubscriber ts3 = new TestSubscriber(); + TestSubscriber ts3 = new TestSubscriber<>(); source.subscribe(ts3); @@ -1287,8 +1358,8 @@ public void subscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableFlowable cf = Flowable.range(1, 3).replay(); - final TestSubscriber ts1 = new TestSubscriber(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); + final TestSubscriber ts2 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -1313,8 +1384,8 @@ public void addRemoveRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableFlowable cf = Flowable.range(1, 3).replay(); - final TestSubscriber ts1 = new TestSubscriber(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); + final TestSubscriber ts2 = new TestSubscriber<>(); cf.subscribe(ts1); @@ -1413,7 +1484,7 @@ public void subscribeOnNextRace() { final ConnectableFlowable cf = pp.replay(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -1442,7 +1513,7 @@ public void unsubscribeOnNextRace() { final ConnectableFlowable cf = pp.replay(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); cf.subscribe(ts1); @@ -1471,7 +1542,7 @@ public void unsubscribeReplayRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableFlowable cf = Flowable.range(1, 1000).replay(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); cf.connect(); @@ -1593,12 +1664,12 @@ public void replayMaxInt() { @Test public void timedAndSizedTruncationError() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, false); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, false); Assert.assertFalse(buf.hasCompleted()); Assert.assertFalse(buf.hasError()); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -1636,8 +1707,8 @@ public void timedAndSizedTruncationError() { @Test public void sizedTruncation() { - SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer(2, false); - List values = new ArrayList(); + SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer<>(2, false); + List values = new ArrayList<>(); buf.next(1); buf.next(2); @@ -1917,19 +1988,6 @@ public ReplayBuffer get() throws Exception { .assertFailure(TestException.class); } - @Test - public void currentDisposedWhenConnecting() { - FlowableReplay fr = (FlowableReplay)FlowableReplay.create(Flowable.never(), 16, false); - fr.connect(); - - fr.current.get().dispose(); - assertTrue(fr.current.get().isDisposed()); - - fr.connect(); - - assertFalse(fr.current.get().isDisposed()); - } - @Test public void noBoundedRetentionViaThreadLocal() throws Exception { Flowable source = Flowable.range(1, 200) @@ -1992,4 +2050,240 @@ public void accept(byte[] v) throws Exception { + " -> " + after.get() / 1024.0 / 1024.0); } } + + @Test + public void unsafeChildOnNextThrowsSizeBound() { + final AtomicInteger count = new AtomicInteger(); + + Flowable source = Flowable.range(1, 100) + .doOnNext(new Consumer() { + @Override + public void accept(Integer t) { + count.getAndIncrement(); + } + }) + .replay(1000).autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(Integer t) { + throw new TestException(); + } + }; + + source.subscribe(ts); + + Assert.assertEquals(100, count.get()); + + ts.assertNoValues(); + ts.assertNotComplete(); + ts.assertError(TestException.class); + } + + @Test + public void unsafeChildOnErrorThrowsSizeBound() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.error(new IOException()) + .replay(1000) + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onError(Throwable t) { + super.onError(t); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertFailure(IOException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void unsafeChildOnCompleteThrowsSizeBound() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable source = Flowable.empty() + .replay(1000) + .autoConnect(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onComplete() { + super.onComplete(); + throw new TestException(); + } + }; + + source.subscribe(ts); + + ts.assertResult(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test(expected = TestException.class) + public void connectDisposeCrash() { + ConnectableFlowable cf = Flowable.never().replay(); + + cf.connect(); + + cf.connect(d -> { throw new TestException(); }); + } + + @Test + public void resetWhileNotConnectedIsNoOp() { + ConnectableFlowable cf = Flowable.never().replay(); + + cf.reset(); + } + + @Test + public void resetWhileActiveIsNoOp() { + ConnectableFlowable cf = Flowable.never().replay(); + + cf.connect(); + + cf.reset(); + } + + @Test + public void delayedUpstreamSubscription() { + AtomicReference> ref = new AtomicReference<>(); + Flowable f = Flowable.unsafeCreate(ref::set); + + TestSubscriber ts = f.replay() + .autoConnect() + .test(); + + AtomicLong requested = new AtomicLong(); + + ref.get().onSubscribe(new Subscription() { + @Override + public void request(long n) { + BackpressureHelper.add(requested, n); + } + + @Override + public void cancel() { + } + }); + + assertEquals(Long.MAX_VALUE, requested.get()); + ref.get().onComplete(); + + ts.assertResult(); + } + + @Test + public void disposeNoNeedForReset() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetSizeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, TimeUnit.MINUTES); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeAndSIzeBound() { + PublishProcessor pp = PublishProcessor.create(); + + ConnectableFlowable cf = pp.replay(10, 10, TimeUnit.MINUTES); + + TestSubscriber ts = cf.test(); + + Disposable d = cf.connect(); + + pp.onNext(1); + + d.dispose(); + + ts = cf.test(); + + ts.assertEmpty(); + + cf.connect(); + + ts.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java index e2db70633a..224ae32d40 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -63,7 +63,7 @@ public void subscribe(Subscriber t1) { } }); - TestSubscriber ts = new TestSubscriber(consumer); + TestSubscriber ts = new TestSubscriber<>(consumer); producer.retryWhen(new Function, Flowable>() { @Override @@ -73,7 +73,7 @@ public Flowable apply(Flowable attempts) { .map(new Function() { @Override public Tuple apply(Throwable n) { - return new Tuple(new Long(1), n); + return new Tuple(1L, n); }}) .scan(new BiFunction() { @Override @@ -117,7 +117,7 @@ public void retryIndefinitely() { Subscriber subscriber = TestHelper.mockSubscriber(); int numRetries = 20; Flowable origin = Flowable.unsafeCreate(new FuncWithErrors(numRetries)); - origin.retry().subscribe(new TestSubscriber(subscriber)); + origin.retry().subscribe(new TestSubscriber<>(subscriber)); InOrder inOrder = inOrder(subscriber); // should show 3 attempts @@ -136,7 +136,7 @@ public void schedulingNotificationHandler() { Subscriber subscriber = TestHelper.mockSubscriber(); int numRetries = 2; Flowable origin = Flowable.unsafeCreate(new FuncWithErrors(numRetries)); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); origin.retryWhen(new Function, Flowable>() { @Override public Flowable apply(Flowable t1) { @@ -203,7 +203,7 @@ public Integer apply(Throwable t1) { public void onCompletedFromNotificationHandler() { Subscriber subscriber = TestHelper.mockSubscriber(); Flowable origin = Flowable.unsafeCreate(new FuncWithErrors(1)); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); origin.retryWhen(new Function, Flowable>() { @Override public Flowable apply(Flowable t1) { @@ -498,7 +498,7 @@ public void cancel() { public void sourceFlowableCallsUnsubscribe() throws InterruptedException { final AtomicInteger subsCount = new AtomicInteger(0); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Publisher onSubscribe = new Publisher() { @Override @@ -529,7 +529,7 @@ public void subscribe(Subscriber s) { public void sourceFlowableRetry1() throws InterruptedException { final AtomicInteger subsCount = new AtomicInteger(0); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Publisher onSubscribe = new Publisher() { @Override @@ -548,7 +548,7 @@ public void subscribe(Subscriber s) { public void sourceFlowableRetry0() throws InterruptedException { final AtomicInteger subsCount = new AtomicInteger(0); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Publisher onSubscribe = new Publisher() { @Override @@ -621,7 +621,7 @@ public void run() { } } - /** Observer for listener on seperate thread. */ + /** Observer for listener on separate thread. */ static final class AsyncSubscriber extends DefaultSubscriber { protected CountDownLatch latch = new CountDownLatch(1); @@ -674,7 +674,7 @@ public void unsubscribeAfterError() { SlowFlowable so = new SlowFlowable(100, 0, "testUnsubscribeAfterError"); Flowable f = Flowable.unsafeCreate(so).retry(5); - AsyncSubscriber async = new AsyncSubscriber(subscriber); + AsyncSubscriber async = new AsyncSubscriber<>(subscriber); f.subscribe(async); @@ -698,7 +698,7 @@ public void timeoutWithRetry() { SlowFlowable sf = new SlowFlowable(100, 10, "testTimeoutWithRetry"); Flowable f = Flowable.unsafeCreate(sf).timeout(80, TimeUnit.MILLISECONDS).retry(5); - AsyncSubscriber async = new AsyncSubscriber(subscriber); + AsyncSubscriber async = new AsyncSubscriber<>(subscriber); f.subscribe(async); @@ -720,7 +720,7 @@ public void retryWithBackpressure() throws InterruptedException { for (int i = 0; i < 400; i++) { Subscriber subscriber = TestHelper.mockSubscriber(); Flowable origin = Flowable.unsafeCreate(new FuncWithErrors(numRetries)); - TestSubscriberEx ts = new TestSubscriberEx(subscriber); + TestSubscriberEx ts = new TestSubscriberEx<>(subscriber); origin.retry().observeOn(Schedulers.computation()).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -751,7 +751,7 @@ public void retryWithBackpressureParallel() throws InterruptedException { } final AtomicInteger timeouts = new AtomicInteger(); - final Map> data = new ConcurrentHashMap>(); + final Map> data = new ConcurrentHashMap<>(); int m = 5000; final CountDownLatch cdl = new CountDownLatch(m); @@ -763,11 +763,11 @@ public void run() { final AtomicInteger nexts = new AtomicInteger(); try { Flowable origin = Flowable.unsafeCreate(new FuncWithErrors(numRetries)); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); origin.retry() .observeOn(Schedulers.computation()).subscribe(ts); ts.awaitDone(2500, TimeUnit.MILLISECONDS); - List onNextEvents = new ArrayList(ts.values()); + List onNextEvents = new ArrayList<>(ts.values()); if (onNextEvents.size() != numRetries + 2) { for (Throwable t : ts.errors()) { onNextEvents.add(t.toString()); @@ -865,7 +865,7 @@ public Flowable apply(GroupedFlowable t1) { return t1.take(1); } }, NUM_MSG) // Must request as many groups as groupBy produces to avoid MBE - .subscribe(new TestSubscriber(subscriber)); + .subscribe(new TestSubscriber<>(subscriber)); InOrder inOrder = inOrder(subscriber); // should show 3 attempts @@ -910,7 +910,7 @@ public Flowable apply(GroupedFlowable t1) { return t1.take(1); } }) - .subscribe(new TestSubscriber(subscriber)); + .subscribe(new TestSubscriber<>(subscriber)); InOrder inOrder = inOrder(subscriber); // should show 3 attempts diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWithPredicateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWithPredicateTest.java index 9a69dd3ede..2180ac8873 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWithPredicateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableRetryWithPredicateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -236,7 +236,7 @@ public void unsubscribeAfterError() { .unsafeCreate(so) .retry(retry5); - FlowableRetryTest.AsyncSubscriber async = new FlowableRetryTest.AsyncSubscriber(subscriber); + FlowableRetryTest.AsyncSubscriber async = new FlowableRetryTest.AsyncSubscriber<>(subscriber); f.subscribe(async); @@ -263,7 +263,7 @@ public void timeoutWithRetry() { .timeout(80, TimeUnit.MILLISECONDS) .retry(retry5); - FlowableRetryTest.AsyncSubscriber async = new FlowableRetryTest.AsyncSubscriber(subscriber); + FlowableRetryTest.AsyncSubscriber async = new FlowableRetryTest.AsyncSubscriber<>(subscriber); f.subscribe(async); @@ -279,7 +279,7 @@ public void timeoutWithRetry() { @Test public void issue2826() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final RuntimeException e = new RuntimeException("You shall not pass"); final AtomicInteger c = new AtomicInteger(); Flowable.just(1).map(new Function() { @@ -313,7 +313,7 @@ public Integer apply(Integer t1) { @Test public void issue3008RetryWithPredicate() { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); final AtomicBoolean isFirst = new AtomicBoolean(true); Flowable. just(1L, 2L, 3L).map(new Function() { @Override @@ -341,7 +341,7 @@ public void accept(Long t) { @Test public void issue3008RetryInfinite() { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); final AtomicBoolean isFirst = new AtomicBoolean(true); Flowable. just(1L, 2L, 3L).map(new Function() { @Override @@ -365,7 +365,7 @@ public void accept(Long t) { @Test public void backpressure() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable source = Flowable .just(1) @@ -377,7 +377,7 @@ public void accept(long t) { } }); - TestSubscriber ts = new TestSubscriber(3L); + TestSubscriber ts = new TestSubscriber<>(3L); source .retry(new BiPredicate() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java index f9ed6509db..1bc6456695 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSampleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -481,4 +481,10 @@ public void badRequest() { TestHelper.assertBadRequestReported(PublishProcessor.create() .sample(PublishProcessor.create())); } + + @Test + public void badRequestTimed() { + TestHelper.assertBadRequestReported(PublishProcessor.create() + .sample(1, TimeUnit.MINUTES)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java index 60ddbbff52..fab387470e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -59,7 +59,7 @@ public Integer get() throws Exception { static final class OneCallablePublisher implements Publisher, Supplier { @Override public void subscribe(Subscriber s) { - s.onSubscribe(new ScalarSubscription(s, 1)); + s.onSubscribe(new ScalarSubscription<>(s, 1)); } @Override @@ -70,7 +70,7 @@ public Integer get() throws Exception { @Test public void tryScalarXMap() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertTrue(FlowableScalarXMap.tryScalarXMapSubscribe(new CallablePublisher(), ts, new Function>() { @Override public Publisher apply(Integer f) throws Exception { @@ -83,7 +83,7 @@ public Publisher apply(Integer f) throws Exception { @Test public void emptyXMap() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertTrue(FlowableScalarXMap.tryScalarXMapSubscribe(new EmptyCallablePublisher(), ts, new Function>() { @Override @@ -97,7 +97,7 @@ public Publisher apply(Integer f) throws Exception { @Test public void mapperCrashes() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertTrue(FlowableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), ts, new Function>() { @Override @@ -111,7 +111,7 @@ public Publisher apply(Integer f) throws Exception { @Test public void mapperToJust() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertTrue(FlowableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), ts, new Function>() { @Override @@ -125,7 +125,7 @@ public Publisher apply(Integer f) throws Exception { @Test public void mapperToEmpty() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertTrue(FlowableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), ts, new Function>() { @Override @@ -139,7 +139,7 @@ public Publisher apply(Integer f) throws Exception { @Test public void mapperToCrashingCallable() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertTrue(FlowableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), ts, new Function>() { @Override @@ -177,8 +177,8 @@ public Publisher apply(Integer v) throws Exception { @Test public void scalarDisposableStateCheck() { - TestSubscriber ts = new TestSubscriber(); - ScalarSubscription sd = new ScalarSubscription(ts, 1); + TestSubscriber ts = new TestSubscriber<>(); + ScalarSubscription sd = new ScalarSubscription<>(ts, 1); ts.onSubscribe(sd); assertFalse(sd.isCancelled()); @@ -211,8 +211,8 @@ public void scalarDisposableStateCheck() { @Test public void scalarDisposableRunDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(); - final ScalarSubscription sd = new ScalarSubscription(ts, 1); + TestSubscriber ts = new TestSubscriber<>(); + final ScalarSubscription sd = new ScalarSubscription<>(ts, 1); ts.onSubscribe(sd); Runnable r1 = new Runnable() { @@ -235,7 +235,7 @@ public void run() { @Test public void cancelled() { - ScalarSubscription scalar = new ScalarSubscription(new TestSubscriber(), 1); + ScalarSubscription scalar = new ScalarSubscription<>(new TestSubscriber<>(), 1); assertFalse(scalar.isCancelled()); @@ -243,4 +243,12 @@ public void cancelled() { assertTrue(scalar.isCancelled()); } + + @Test + public void mapToNonScalar() { + Flowable.fromCallable(() -> 1) + .concatMap(v -> Flowable.range(1, 5)) + .test() + .assertResult(1, 2, 3, 4, 5); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java index f918d152be..5c6e4fa901 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableScanTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -114,7 +114,7 @@ public Integer apply(Integer t1, Integer t2) { @Test public void shouldNotEmitUntilAfterSubscription() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 100).scan(0, new BiFunction() { @Override @@ -263,7 +263,7 @@ public void seedFactory() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @@ -289,7 +289,7 @@ public void seedFactoryFlowable() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @@ -315,7 +315,7 @@ public Integer apply(Integer t1, Integer t2) { } }).take(1); - TestSubscriberEx subscriber = new TestSubscriberEx(); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); f.subscribe(subscriber); subscriber.assertValue(0); subscriber.assertTerminated(); @@ -324,7 +324,7 @@ public Integer apply(Integer t1, Integer t2) { @Test public void scanShouldNotRequestZero() { - final AtomicReference producer = new AtomicReference(); + final AtomicReference producer = new AtomicReference<>(); Flowable f = Flowable.unsafeCreate(new Publisher() { @Override public void subscribe(final Subscriber subscriber) { @@ -445,7 +445,7 @@ public Integer apply(Integer a, Integer b) throws Exception { public void unsubscribeScan() { FlowableEventStream.getEventStream("HTTP-ClusterB", 20) - .scan(new HashMap(), new BiFunction, Event, HashMap>() { + .scan(new HashMap<>(), new BiFunction, Event, HashMap>() { @Override public HashMap apply(HashMap accum, Event perInstanceEvent) { accum.put("instance", perInstanceEvent.instanceId); @@ -463,7 +463,7 @@ public void accept(HashMap v) { @Test public void scanWithSeedDoesNotEmitErrorTwiceIfScanFunctionThrows() { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); Consumer errorConsumer = new Consumer() { @Override public void accept(Throwable t) throws Exception { @@ -543,7 +543,7 @@ public void scanNoSeed() { @Test public void scanNoSeedDoesNotEmitErrorTwiceIfScanFunctionThrows() { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); Consumer errorConsumer = new Consumer() { @Override public void accept(Throwable t) throws Exception { @@ -676,4 +676,27 @@ public Integer apply(Integer a, Integer b) throws Exception { } } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().scanWith(() -> 1, (a, b) -> a + b)); + } + + @Test + public void drainMoreWork() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.scanWith(() -> 0, (a, b) -> a + b) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(1); + + ts.assertResult(0, 1, 3); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java index e94bf06167..c73b57777b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSequenceEqualTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -384,7 +384,7 @@ protected void subscribeActual(Subscriber s) { }; for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final PublishProcessor pp = PublishProcessor.create(); @@ -487,7 +487,7 @@ protected void subscribeActual(Subscriber s) { }; for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final PublishProcessor pp = PublishProcessor.create(); @@ -591,4 +591,26 @@ public Flowable apply(Flowable upstream) { } }); } + + @Test + public void fusionRejected() { + Flowable.sequenceEqual(TestHelper.rejectFlowableFusion(), Flowable.never()) + .test() + .assertEmpty(); + } + + @Test + public void fusionRejectedFlowable() { + Flowable.sequenceEqual(TestHelper.rejectFlowableFusion(), Flowable.never()) + .toFlowable() + .test() + .assertEmpty(); + } + + @Test + public void asyncSourceCompare() { + Flowable.sequenceEqual(Flowable.fromCallable(() -> 1), Flowable.just(1)) + .test() + .assertResult(true); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java index cd848e683d..913c598126 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSerializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -79,7 +79,22 @@ public void multiThreadedBasic() { } @Test - public void multiThreadedWithNPE() { + public void multiThreadedWithNPEFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPE(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPE() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null); Flowable w = Flowable.unsafeCreate(onSubscribe); @@ -108,7 +123,22 @@ public void multiThreadedWithNPE() { } @Test - public void multiThreadedWithNPEinMiddle() { + public void multiThreadedWithNPEinMiddleFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPEinMiddle(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPEinMiddle() { boolean lessThan9 = false; for (int i = 0; i < 3; i++) { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java index 3e2fdae2a0..85fbfbc00d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -74,7 +74,7 @@ public void singleWithEmptyFlowable() { @Test public void singleDoesNotRequestMoreThanItNeedsIf1Then2RequestedFlowable() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.just(1) // .doOnRequest(new LongConsumer() { @@ -115,7 +115,7 @@ public void onNext(Integer t) { @Test public void singleDoesNotRequestMoreThanItNeedsIf3RequestedFlowable() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.just(1) // .doOnRequest(new LongConsumer() { @@ -155,7 +155,7 @@ public void onNext(Integer t) { @Test public void singleRequestsExactlyWhatItNeedsIf1RequestedFlowable() { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); Flowable.just(1) // .doOnRequest(new LongConsumer() { @@ -705,7 +705,7 @@ public Integer apply(Integer i1, Integer i2) { @Test public void singleElementOperatorDoNotSwallowExceptionWhenDone() { final Throwable exception = new RuntimeException("some error"); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); try { RxJavaPlugins.setErrorHandler(new Consumer() { @@ -805,4 +805,9 @@ public void singleOrError() { .test() .assertFailure(NoSuchElementException.class); } + + @Test + public void dispose() { + TestHelper.checkDisposed(PublishProcessor.create().single(1)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTest.java index be4230486a..69eac72c54 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -89,7 +89,7 @@ public void skipLastWithZeroCount() { @Test public void skipLastWithBackpressure() { Flowable f = Flowable.range(0, Flowable.bufferSize() * 2).skipLast(Flowable.bufferSize() + 10); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); f.observeOn(Schedulers.computation()).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertNoErrors(); @@ -97,7 +97,7 @@ public void skipLastWithBackpressure() { } - @Test(expected = IndexOutOfBoundsException.class) + @Test(expected = IllegalArgumentException.class) public void skipLastWithNegativeCount() { Flowable.just("one").skipLast(-1); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java index 87f4ddf881..f2f1ff2598 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipLastTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -250,4 +250,27 @@ public void observeOn() { .assertComplete() .assertNoErrors(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().skipLast(1, TimeUnit.MINUTES)); + } + + @Test + public void delayErrorMoreWork() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.skipLast(0, TimeUnit.MILLISECONDS, true) + .doOnNext(v -> { + if (v == 1) { + pp.onNext(1); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(1); + + ts.assertComplete(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTest.java index 26128e5889..eac3c94df8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public class FlowableSkipTest extends RxJavaTest { - @Test + @Test(expected = IllegalArgumentException.class) public void skipNegativeElements() { Flowable skip = Flowable.just("one", "two", "three").skip(-99); @@ -143,7 +143,7 @@ public void skipError() { @Test public void backpressureMultipleSmallAsyncRequests() throws InterruptedException { final AtomicLong requests = new AtomicLong(0); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.interval(100, TimeUnit.MILLISECONDS) .doOnRequest(new LongConsumer() { @Override @@ -162,7 +162,7 @@ public void accept(long n) { @Test public void requestOverflowDoesNotOccur() { - TestSubscriberEx ts = new TestSubscriberEx(Long.MAX_VALUE - 1); + TestSubscriberEx ts = new TestSubscriberEx<>(Long.MAX_VALUE - 1); Flowable.range(1, 10).skip(5).subscribe(ts); ts.assertTerminated(); ts.assertComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTimedTest.java index 74532ad63c..82012097e7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntilTest.java index b579f0e5d6..d5bbdf969a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhileTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhileTest.java index 3ab4f9af64..3fd42350d2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhileTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSkipWhileTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableStartWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableStartWithTest.java new file mode 100644 index 0000000000..6aa442c00b --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableStartWithTest.java @@ -0,0 +1,153 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.flowable; + +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class FlowableStartWithTest { + + @Test + public void justCompletableComplete() { + Flowable.just(1).startWith(Completable.complete()) + .test() + .assertResult(1); + } + + @Test + public void emptyCompletableComplete() { + Flowable.empty().startWith(Completable.complete()) + .test() + .assertResult(); + } + + @Test + public void runCompletableError() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run).startWith(Completable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justSingleJust() { + Flowable.just(1).startWith(Single.just(2)) + .test() + .assertResult(2, 1); + } + + @Test + public void emptySingleJust() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run) + .startWith(Single.just(2)) + .test() + .assertResult(2); + + verify(run).run(); + } + + @Test + public void runSingleError() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run).startWith(Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justMaybeJust() { + Flowable.just(1).startWith(Maybe.just(2)) + .test() + .assertResult(2, 1); + } + + @Test + public void emptyMaybeJust() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run) + .startWith(Maybe.just(2)) + .test() + .assertResult(2); + + verify(run).run(); + } + + @Test + public void runMaybeError() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run).startWith(Maybe.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justFlowableJust() { + Flowable.just(1).startWith(Flowable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5, 1); + } + + @Test + public void emptyFlowableJust() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run) + .startWith(Flowable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5); + + verify(run).run(); + } + + @Test + public void emptyFlowableEmpty() { + Runnable run = mock(Runnable.class); + Runnable run2 = mock(Runnable.class); + + Flowable.fromRunnable(run) + .startWith(Flowable.fromRunnable(run2)) + .test() + .assertResult(); + + verify(run).run(); + verify(run2).run(); + } + + @Test + public void runFlowableError() { + Runnable run = mock(Runnable.class); + + Flowable.fromRunnable(run).startWith(Flowable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java index a10283358e..01ee614fd5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,6 +27,7 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.operators.flowable.FlowableSubscribeOn.SubscribeOnSubscriber; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; @@ -41,7 +42,7 @@ public void issue813() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch doneLatch = new CountDownLatch(1); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable .unsafeCreate(new Publisher() { @@ -79,7 +80,7 @@ public void subscribe( @Test public void onError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.unsafeCreate(new Publisher() { @Override @@ -152,7 +153,7 @@ public Disposable schedule(@NonNull final Runnable action, final long delayTime, @Test public void unsubscribeInfiniteStream() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger count = new AtomicInteger(); Flowable.unsafeCreate(new Publisher() { @@ -178,7 +179,7 @@ public void subscribe(Subscriber sub) { @Test public void backpressureReschedulesCorrectly() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(10); - TestSubscriberEx ts = new TestSubscriberEx(new DefaultSubscriber() { + TestSubscriberEx ts = new TestSubscriberEx<>(new DefaultSubscriber() { @Override public void onComplete() { @@ -201,14 +202,14 @@ public void onNext(Integer t) { System.out.println("First schedule: " + t); assertTrue(t.getName().startsWith("Rx")); ts.request(10); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(20, TimeUnit.SECONDS); System.out.println("After reschedule: " + ts.lastThread()); assertEquals(t, ts.lastThread()); } @Test public void setProducerSynchronousRequest() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2, 3).lift(new FlowableOperator() { @Override @@ -253,7 +254,7 @@ public void onNext(Integer t) { } }).subscribeOn(Schedulers.newThread()).subscribe(ts); - ts.awaitDone(5, TimeUnit.SECONDS); + ts.awaitDone(20, TimeUnit.SECONDS); ts.assertNoErrors(); } @@ -283,11 +284,11 @@ public void dispose() { public void deferredRequestRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Worker w = Schedulers.computation().createWorker(); - final SubscribeOnSubscriber so = new SubscribeOnSubscriber(ts, w, Flowable.never(), true); + final SubscribeOnSubscriber so = new SubscribeOnSubscriber<>(ts, w, Flowable.never(), true); ts.onSubscribe(so); final BooleanSubscription bs = new BooleanSubscription(); @@ -329,7 +330,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single()) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertNoErrors() .assertComplete(); @@ -354,7 +355,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single()) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertValueCount(Flowable.bufferSize()) .assertNoErrors() .assertComplete(); @@ -376,7 +377,7 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single(), false) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertNoErrors() .assertComplete(); @@ -401,9 +402,14 @@ public void subscribe(FlowableEmitter s) throws Exception { .subscribeOn(Schedulers.single(), true) .observeOn(Schedulers.computation()) .test() - .awaitDone(5, TimeUnit.SECONDS) + .awaitDone(20, TimeUnit.SECONDS) .assertValueCount(Flowable.bufferSize()) .assertNoErrors() .assertComplete(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().subscribeOn(ImmediateThinScheduler.INSTANCE)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmptyTest.java index 9ee55f4dfa..d38a657b99 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchIfEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -142,7 +142,7 @@ public void subscribe(final Subscriber subscriber) { @Test public void switchRequestAlternativeObservableWithBackpressure() { - TestSubscriber ts = new TestSubscriber(1L); + TestSubscriber ts = new TestSubscriber<>(1L); Flowable.empty().switchIfEmpty(Flowable.just(1, 2, 3)).subscribe(ts); @@ -156,7 +156,7 @@ public void switchRequestAlternativeObservableWithBackpressure() { @Test public void backpressureNoRequest() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.empty().switchIfEmpty(Flowable.just(1, 2, 3)).subscribe(ts); ts.assertNoValues(); ts.assertNoErrors(); @@ -164,7 +164,7 @@ public void backpressureNoRequest() { @Test public void backpressureOnFirstObservable() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.just(1, 2, 3).switchIfEmpty(Flowable.just(4, 5, 6)).subscribe(ts); ts.assertNotComplete(); ts.assertNoErrors(); @@ -173,7 +173,7 @@ public void backpressureOnFirstObservable() { @Test public void requestsNotLost() throws InterruptedException { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Flowable.unsafeCreate(new Publisher() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java index fa83213599..9f200e3c56 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.*; import org.junit.*; import org.mockito.InOrder; @@ -32,7 +32,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; import io.reactivex.rxjava3.testsupport.*; @@ -450,7 +450,7 @@ public void backpressure() { publishCompleted(o2, 50); publishCompleted(o3, 55); - final TestSubscriberEx testSubscriber = new TestSubscriberEx(); + final TestSubscriberEx testSubscriber = new TestSubscriberEx<>(); Flowable.switchOnNext(o).subscribe(new DefaultSubscriber() { private int requested; @@ -549,7 +549,7 @@ public void onNext(String t) { @Test public void initialRequestsAreAdditive() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.switchOnNext( Flowable.interval(100, TimeUnit.MILLISECONDS) .map( @@ -568,7 +568,7 @@ public Flowable apply(Long t) { @Test public void initialRequestsDontOverflow() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.switchOnNext( Flowable.interval(100, TimeUnit.MILLISECONDS) .map(new Function>() { @@ -585,7 +585,7 @@ public Flowable apply(Long t) { @Test public void secondaryRequestsDontOverflow() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.switchOnNext( Flowable.interval(100, TimeUnit.MILLISECONDS) .map(new Function>() { @@ -639,7 +639,7 @@ public void delayErrors() { @Test public void switchOnNextPrefetch() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable source = Flowable.range(1, 10).hide().doOnNext(new Consumer() { @Override @@ -656,7 +656,7 @@ public void accept(Integer v) throws Exception { @Test public void switchOnNextDelayError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable source = Flowable.range(1, 10).hide().doOnNext(new Consumer() { @Override @@ -673,7 +673,7 @@ public void accept(Integer v) throws Exception { @Test public void switchOnNextDelayErrorPrefetch() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable source = Flowable.range(1, 10).hide().doOnNext(new Consumer() { @Override @@ -1076,13 +1076,13 @@ protected void subscribeActual(Subscriber s) { } }), 8) .test(1L) - .assertFailure(MissingBackpressureException.class, 0); + .assertFailure(QueueOverflowException.class, 0); } @Test public void drainCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final PublishProcessor pp = PublishProcessor.create(); @@ -1176,7 +1176,7 @@ public Object apply(Integer w) throws Exception { public void undeliverableUponCancel() { List errors = TestHelper.trackPluginErrors(); try { - final TestSubscriberEx ts = new TestSubscriberEx(); + final TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(1) .map(new Function() { @@ -1229,4 +1229,167 @@ public Publisher apply(Integer v) .test() .assertResult(10, 20); } + + @Test + public void asyncFusedInner() { + Flowable.just(1) + .hide() + .switchMap(v -> Flowable.fromCallable(() -> 1)) + .test() + .assertResult(1); + } + + @Test + public void innerIgnoresCancelAndErrors() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp + .switchMap(v -> { + if (v == 1) { + return Flowable.unsafeCreate(s -> { + s.onSubscribe(new BooleanSubscription()); + pp.onNext(2); + s.onError(new TestException()); + }); + } + return Flowable.never(); + }) + .test(); + + pp.onNext(1); + + ts.assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.switchMap(v -> Flowable.never())); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().switchMap(v -> Flowable.never())); + } + + @Test + public void innerFailed() { + BehaviorProcessor.createDefault(Flowable.error(new TestException())) + .switchMap(v -> v) + .test() + .assertFailure(TestException.class) + ; + } + + @Test + public void innerCompleted() { + BehaviorProcessor.createDefault(Flowable.empty().hide()) + .switchMap(v -> v) + .test() + .assertEmpty() + ; + } + + @Test + public void innerCompletedBackpressureBoundary() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = BehaviorProcessor.createDefault(pp) + .onBackpressureBuffer() + .switchMap(v -> v) + .test(1L) + ; + + ts.assertEmpty(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertValuesOnly(1); + } + + @Test + public void innerCompletedDelayError() { + BehaviorProcessor.createDefault(Flowable.empty().hide()) + .switchMapDelayError(v -> v) + .test() + .assertEmpty() + ; + } + + @Test + public void innerCompletedBackpressureBoundaryDelayError() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = BehaviorProcessor.createDefault(pp) + .onBackpressureBuffer() + .switchMapDelayError(v -> v) + .test(1L) + ; + + ts.assertEmpty(); + + pp.onNext(1); + pp.onComplete(); + + ts.assertValuesOnly(1); + } + + @Test + public void cancellationShouldTriggerInnerCancellationRace() throws Throwable { + AtomicInteger outer = new AtomicInteger(); + AtomicInteger inner = new AtomicInteger(); + + int n = 10_000; + for (int i = 0; i < n; i++) { + Flowable.create(it -> { + it.onNext(0); + }, BackpressureStrategy.MISSING) + .switchMap(v -> createFlowable(inner)) + .observeOn(Schedulers.computation()) + .doFinally(() -> { + outer.incrementAndGet(); + }) + .take(1) + .blockingSubscribe(v -> { }, Throwable::printStackTrace); + } + + Thread.sleep(100); + assertEquals(inner.get(), outer.get()); + assertEquals(n, inner.get()); + } + + Flowable createFlowable(AtomicInteger inner) { + return Flowable.unsafeCreate(s -> { + SerializedSubscriber it = new SerializedSubscriber<>(s); + it.onSubscribe(new BooleanSubscription()); + Schedulers.io().scheduleDirect(() -> { + it.onNext(1); + }, 0, TimeUnit.MILLISECONDS); + Schedulers.io().scheduleDirect(() -> { + it.onNext(2); + }, 0, TimeUnit.MILLISECONDS); + }) + .doFinally(() -> { + inner.incrementAndGet(); + }); + } + + @Test + public void innerOnSubscribeOuterCancelRace() { + TestSubscriber ts = new TestSubscriber(); + + Flowable.just(1) + .hide() + .switchMap(v -> Flowable.just(1) + .doOnSubscribe(d -> ts.cancel()) + .scan(1, (a, b) -> a) + ) + .subscribe(ts); + + ts.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOneTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOneTest.java index 8acf9c3026..1e51a40249 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOneTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastOneTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class FlowableTakeLastOneTest extends RxJavaTest { @Test public void lastOfManyReturnsLast() { - TestSubscriberEx s = new TestSubscriberEx(); + TestSubscriberEx s = new TestSubscriberEx<>(); Flowable.range(1, 10).takeLast(1).subscribe(s); s.assertValue(10); s.assertNoErrors(); @@ -41,7 +41,7 @@ public void lastOfManyReturnsLast() { @Test public void lastOfEmptyReturnsEmpty() { - TestSubscriberEx s = new TestSubscriberEx(); + TestSubscriberEx s = new TestSubscriberEx<>(); Flowable.empty().takeLast(1).subscribe(s); s.assertNoValues(); s.assertNoErrors(); @@ -52,7 +52,7 @@ public void lastOfEmptyReturnsEmpty() { @Test public void lastOfOneReturnsLast() { - TestSubscriberEx s = new TestSubscriberEx(); + TestSubscriberEx s = new TestSubscriberEx<>(); Flowable.just(1).takeLast(1).subscribe(s); s.assertValue(1); s.assertNoErrors(); @@ -81,7 +81,7 @@ public void run() { @Test public void lastWithBackpressure() { - MySubscriber s = new MySubscriber(0); + MySubscriber s = new MySubscriber<>(0); Flowable.just(1).takeLast(1).subscribe(s); assertEquals(0, s.list.size()); s.requestMore(1); @@ -111,7 +111,7 @@ private static class MySubscriber extends DefaultSubscriber { this.initialRequest = initialRequest; } - final List list = new ArrayList(); + final List list = new ArrayList<>(); public void requestMore(long n) { request(n); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java index 764e932405..6008461ed5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,8 +23,9 @@ import org.junit.Test; import org.mockito.InOrder; -import org.reactivestreams.Subscriber; +import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; @@ -89,14 +90,14 @@ public void takeLastWithZeroCount() { verify(subscriber, times(1)).onComplete(); } - @Test(expected = IndexOutOfBoundsException.class) + @Test(expected = IllegalArgumentException.class) public void takeLastWithNegativeCount() { Flowable.just("one").takeLast(-1); } @Test public void backpressure1() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 100000).takeLast(1) .observeOn(Schedulers.newThread()) .map(newSlowProcessor()).subscribe(ts); @@ -107,7 +108,7 @@ public void backpressure1() { @Test public void backpressure2() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 100000).takeLast(Flowable.bufferSize() * 4) .observeOn(Schedulers.newThread()).map(newSlowProcessor()).subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -283,7 +284,7 @@ public void onNext(Integer integer) { @Test public void requestOverflow() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Flowable.range(1, 100).takeLast(50).subscribe(new DefaultSubscriber() { @Override @@ -340,4 +341,54 @@ public void takeLastTake() { .test() .assertResult(6, 7); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().takeLast(2)); + } + + @Test + public void cancelThenRequest() { + Flowable.never().takeLast(2) + .subscribe(new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Object t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + s.cancel(); + s.request(1); + } + }); + } + + @Test + public void noRequestEmpty() { + Flowable.empty() + .takeLast(2) + .test(0L) + .assertResult(); + } + + @Test + public void moreValuesRemainingThanRequested() { + Flowable.range(1, 4) + .takeLast(3) + .test(0L) + .assertEmpty() + .requestMore(2) + .assertValuesOnly(2, 3) + .requestMore(2) + .assertResult(2, 3, 4); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimedTest.java index 745cdf01dd..83814dd797 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeLastTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public class FlowableTakeLastTimedTest extends RxJavaTest { - @Test(expected = IndexOutOfBoundsException.class) + @Test(expected = IllegalArgumentException.class) public void takeLastTimedWithNegativeCount() { Flowable.just("one").takeLast(-1, 1, TimeUnit.SECONDS); } @@ -208,7 +208,7 @@ public void takeLastTimedWithZeroCapacity() { public void continuousDelivery() { TestScheduler scheduler = new TestScheduler(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); PublishProcessor pp = PublishProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest.java index 3174c62721..50728f6206 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -290,7 +290,7 @@ public void subscribe(Subscriber op) { @Test public void takeObserveOn() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); INFINITE_OBSERVABLE.onBackpressureDrop() .observeOn(Schedulers.newThread()).take(1).subscribe(ts); @@ -305,7 +305,7 @@ public void takeObserveOn() { @Test public void producerRequestThroughTake() { - TestSubscriber ts = new TestSubscriber(3); + TestSubscriber ts = new TestSubscriber<>(3); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -331,7 +331,7 @@ public void cancel() { @Test public void producerRequestThroughTakeIsModified() { - TestSubscriber ts = new TestSubscriber(3); + TestSubscriber ts = new TestSubscriber<>(3); final AtomicLong requested = new AtomicLong(); Flowable.unsafeCreate(new Publisher() { @@ -358,7 +358,7 @@ public void cancel() { @Test public void interrupt() throws InterruptedException { - final AtomicReference exception = new AtomicReference(); + final AtomicReference exception = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); Flowable.just(1).subscribeOn(Schedulers.computation()).take(1) .subscribe(new Consumer() { @@ -384,7 +384,7 @@ public void accept(Integer t1) { @Test public void doesntRequestMoreThanNeededFromUpstream() throws InterruptedException { final AtomicLong requests = new AtomicLong(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.interval(100, TimeUnit.MILLISECONDS) // .doOnRequest(new LongConsumer() { @@ -429,7 +429,7 @@ public void onNext(Integer t) { public void reentrantTake() { final PublishProcessor source = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.take(1).doOnNext(new Consumer() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest2.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest2.java index 14987ce16a..da2aa5657e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest2.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTest2.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ // moved tests from FlowableLimitTest to here (limit removed as operator) public class FlowableTakeTest2 extends RxJavaTest implements LongConsumer, Action { - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); static final Long CANCELLED = -100L; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTimedTest.java index 34d39b125a..776236a7cd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicateTest.java index 4bdda03eb1..f88b7e27ae 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilPredicateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -142,7 +142,7 @@ public boolean test(Integer v) { @Test public void backpressure() { - TestSubscriber ts = new TestSubscriber(5L); + TestSubscriber ts = new TestSubscriber<>(5L); Flowable.range(1, 1000).takeUntil(new Predicate() { @Override @@ -158,7 +158,7 @@ public boolean test(Integer v) { @Test public void errorIncludesLastValueAsCause() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final TestException e = new TestException("Forced failure"); Predicate predicate = new Predicate() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilTest.java index 7db84c13d8..a1f7fe9d42 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -190,7 +190,7 @@ public void untilFires() { PublishProcessor source = PublishProcessor.create(); PublishProcessor until = PublishProcessor.create(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.takeUntil(until).subscribe(ts); @@ -216,7 +216,7 @@ public void mainCompletes() { PublishProcessor source = PublishProcessor.create(); PublishProcessor until = PublishProcessor.create(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.takeUntil(until).subscribe(ts); @@ -240,7 +240,7 @@ public void downstreamUnsubscribes() { PublishProcessor source = PublishProcessor.create(); PublishProcessor until = PublishProcessor.create(); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); source.takeUntil(until).take(1).subscribe(ts); @@ -262,7 +262,7 @@ public void downstreamUnsubscribes() { public void backpressure() { PublishProcessor until = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.range(1, 10).takeUntil(until).subscribe(ts); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhileTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhileTest.java index 90f9cc4a59..805420f094 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhileTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTakeWhileTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -236,7 +236,7 @@ public boolean test(Integer t1) { return t1 < 100; } }); - TestSubscriber ts = new TestSubscriber(5L); + TestSubscriber ts = new TestSubscriber<>(5L); source.subscribe(ts); @@ -257,7 +257,7 @@ public boolean test(Integer t1) { return t1 < 2; } }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); source.subscribe(ts); @@ -269,7 +269,7 @@ public boolean test(Integer t1) { @Test public void errorCauseIncludesLastValue() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just("abc").takeWhile(new Predicate() { @Override public boolean test(String t1) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java index d26d49019a..68a559e3cd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleFirstTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,6 +19,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import io.reactivex.rxjava3.functions.Action; import org.junit.*; import org.mockito.InOrder; import org.reactivestreams.*; @@ -44,6 +45,77 @@ public void before() { subscriber = TestHelper.mockSubscriber(); } + @Test + public void throttlingWithDropCallbackCrashes() throws Throwable { + Flowable source = Flowable.unsafeCreate(new Publisher() { + @Override + public void subscribe(Subscriber subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + publishNext(subscriber, 100, "one"); // publish as it's first + publishNext(subscriber, 300, "two"); // skip as it's last within the first 400 + publishNext(subscriber, 900, "three"); // publish + publishNext(subscriber, 905, "four"); // skip + publishCompleted(subscriber, 1000); // Should be published as soon as the timeout expires. + } + }); + + Action whenDisposed = mock(Action.class); + + Flowable sampled = source + .doOnCancel(whenDisposed) + .throttleFirst(400, TimeUnit.MILLISECONDS, scheduler, e -> { + if ("two".equals(e)) { + throw new TestException("forced"); + } + }); + sampled.subscribe(subscriber); + + InOrder inOrder = inOrder(subscriber); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(subscriber, times(1)).onNext("one"); + inOrder.verify(subscriber, times(1)).onError(any(TestException.class)); + inOrder.verify(subscriber, times(0)).onNext("two"); + inOrder.verify(subscriber, times(0)).onNext("three"); + inOrder.verify(subscriber, times(0)).onNext("four"); + inOrder.verify(subscriber, times(0)).onComplete(); + inOrder.verifyNoMoreInteractions(); + verify(whenDisposed).run(); + } + + @Test + public void throttlingWithDropCallback() { + Flowable source = Flowable.unsafeCreate(new Publisher() { + @Override + public void subscribe(Subscriber subscriber) { + subscriber.onSubscribe(new BooleanSubscription()); + publishNext(subscriber, 100, "one"); // publish as it's first + publishNext(subscriber, 300, "two"); // skip as it's last within the first 400 + publishNext(subscriber, 900, "three"); // publish + publishNext(subscriber, 905, "four"); // skip + publishCompleted(subscriber, 1000); // Should be published as soon as the timeout expires. + } + }); + + Observer dropCallbackObserver = TestHelper.mockObserver(); + Flowable sampled = source.throttleFirst(400, TimeUnit.MILLISECONDS, scheduler, dropCallbackObserver::onNext); + sampled.subscribe(subscriber); + + InOrder inOrder = inOrder(subscriber); + InOrder dropCallbackOrder = inOrder(dropCallbackObserver); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(subscriber, times(1)).onNext("one"); + inOrder.verify(subscriber, times(0)).onNext("two"); + dropCallbackOrder.verify(dropCallbackObserver, times(1)).onNext("two"); + inOrder.verify(subscriber, times(1)).onNext("three"); + inOrder.verify(subscriber, times(0)).onNext("four"); + dropCallbackOrder.verify(dropCallbackObserver, times(1)).onNext("four"); + inOrder.verify(subscriber, times(1)).onComplete(); + inOrder.verifyNoMoreInteractions(); + dropCallbackOrder.verifyNoMoreInteractions(); + } + @Test public void throttlingWithCompleted() { Flowable source = Flowable.unsafeCreate(new Publisher() { @@ -200,4 +272,14 @@ public void backpressureNoRequest() { .test(0L) .assertFailure(MissingBackpressureException.class); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().throttleFirst(1, TimeUnit.MINUTES)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.throttleFirst(1, TimeUnit.MINUTES)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatestTest.java index 45457f5577..2b26ec98d4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableThrottleLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,10 +23,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subscribers.TestSubscriber; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableThrottleLatestTest extends RxJavaTest { @@ -278,4 +279,480 @@ public void onNext(Integer t) { ts.assertResult(1, 2); } + + /** Emit 1, 2, 3, then advance time by a second; 1 and 3 should end up in downstream, 2 should be dropped. */ + @Test + public void onDroppedBasicNoEmitLast() { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriber ts = pp.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + ts.assertEmpty(); + drops.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onNext(3); + + ts.assertValuesOnly(1); + drops.assertValuesOnly(2); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + ts.assertValuesOnly(1, 3); + drops.assertValuesOnly(2); + + pp.onComplete(); + + ts.assertResult(1, 3); + + drops.assertValuesOnly(2); + } + + /** Emit 1, 2, 3; 1 should end up in downstream, 2, 3 should be dropped. */ + @Test + public void onDroppedBasicNoEmitLastDropLast() { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriber ts = pp.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + ts.assertEmpty(); + drops.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onNext(3); + + ts.assertValuesOnly(1); + drops.assertValuesOnly(2); + + pp.onComplete(); + + ts.assertResult(1); + + drops.assertValuesOnly(2, 3); + } + + /** Emit 1, 2, 3; 1 and 3 should end up in downstream, 2 should be dropped. */ + @Test + public void onDroppedBasicEmitLast() { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriber ts = pp.throttleLatest(1, TimeUnit.SECONDS, sch, true, drops::onNext) + .test(); + + ts.assertEmpty(); + drops.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onNext(2); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onNext(3); + + ts.assertValuesOnly(1); + drops.assertValuesOnly(2); + + pp.onComplete(); + + ts.assertResult(1, 3); + + drops.assertValuesOnly(2); + } + + /** Emit 1, 2, 3; 3 should trigger an error to the downstream because 2 is dropped and the callback crashes. */ + @Test + public void onDroppedBasicNoEmitLastFirstDropCrash() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriber ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { + if (d == 2) { + throw new TestException("forced"); + } + }) + .test(); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + pp.onNext(2); + + ts.assertValuesOnly(1); + + pp.onNext(3); + + ts.assertFailure(TestException.class, 1); + + verify(whenDisposed).run(); + } + + /** + * Emit 1, 2, Error; the error should trigger the drop callback and crash it too, + * downstream gets 1, composite(source, drop-crash). + */ + @Test + public void onDroppedBasicNoEmitLastOnErrorDropCrash() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestSubscriberEx<>()); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + pp.onNext(2); + + ts.assertValuesOnly(1); + + pp.onError(new TestException("source")); + + ts.assertFailure(CompositeException.class, 1); + + TestHelper.assertCompositeExceptions(ts, TestException.class, "source", TestException.class, "forced 2"); + + verify(whenDisposed, never()).run(); + } + + /** + * Emit 1, 2, 3; 3 should trigger a drop-crash for 2, which then would trigger the error path and drop-crash for 3, + * the last item not delivered, downstream gets 1, composite(drop-crash 2, drop-crash 3). + */ + @Test + public void onDroppedBasicEmitLastOnErrorDropCrash() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, true, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestSubscriberEx<>()); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + pp.onNext(2); + + ts.assertValuesOnly(1); + + pp.onNext(3); + + ts.assertFailure(CompositeException.class, 1); + + TestHelper.assertCompositeExceptions(ts, TestException.class, "forced 2", TestException.class, "forced 3"); + + verify(whenDisposed).run(); + } + + /** Emit 1, complete; Downstream gets 1, complete, no drops. */ + @Test + public void onDroppedBasicNoEmitLastNoLastToDrop() { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriber ts = pp.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + ts.assertEmpty(); + drops.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onComplete(); + + ts.assertResult(1); + drops.assertEmpty(); + } + + /** Emit 1, error; Downstream gets 1, error, no drops. */ + @Test + public void onDroppedErrorNoEmitLastNoLastToDrop() { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriber ts = pp.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + ts.assertEmpty(); + drops.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + pp.onError(new TestException()); + + ts.assertFailure(TestException.class, 1); + drops.assertEmpty(); + } + + /** + * Emit 1, 2, complete; complete should crash drop, downstream gets 1, drop-crash 2. + */ + @Test + public void onDroppedHasLastNoEmitLastDropCrash() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestSubscriberEx<>()); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + pp.onNext(2); + + ts.assertValuesOnly(1); + + pp.onComplete(); + + ts.assertFailureAndMessage(TestException.class, "forced 2", 1); + + verify(whenDisposed, never()).run(); + } + + /** + * Emit 1, 2 then dispose the sequence; downstream gets 1, drop should get for 2. + */ + @Test + public void onDroppedDisposeDrops() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .subscribeWith(new TestSubscriberEx<>()); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + pp.onNext(2); + + ts.assertValuesOnly(1); + + ts.cancel(); + + ts.assertValuesOnly(1); + drops.assertValuesOnly(2); + + verify(whenDisposed).run(); + } + + /** + * Emit 1 then dispose the sequence; downstream gets 1, drop should not get called. + */ + @Test + public void onDroppedDisposeNoDrops() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .subscribeWith(new TestSubscriberEx<>()); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + ts.cancel(); + + ts.assertValuesOnly(1); + drops.assertEmpty(); + + verify(whenDisposed).run(); + } + + /** + * Emit 1, 2 then dispose the sequence; downstream gets 1, global error handler should get drop-crash 2. + */ + @Test + public void onDroppedDisposeCrashesDrop() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestSubscriberEx<>()); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertValuesOnly(1); + + pp.onNext(2); + + ts.assertValuesOnly(1); + + ts.cancel(); + + ts.assertValuesOnly(1); + + verify(whenDisposed).run(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "forced 2"); + }); + } + + /** Emit 1 but downstream is backpressured; downstream gets MBE, drops gets 1. */ + @Test + public void onDroppedBackpressured() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + TestSubscriber drops = new TestSubscriber<>(); + drops.onSubscribe(EmptySubscription.INSTANCE); + + Action whenDisposed = mock(Action.class); + + TestSubscriber ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(0L); + + ts.assertEmpty(); + drops.assertEmpty(); + + pp.onNext(1); + + ts.assertFailure(MissingBackpressureException.class); + + drops.assertValuesOnly(1); + + verify(whenDisposed).run(); + } + + /** Emit 1 but downstream is backpressured; drop crashes, downstream gets composite(MBE, drop-crash 1). */ + @Test + public void onDroppedBackpressuredDropCrash() throws Throwable { + PublishProcessor pp =PublishProcessor.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestSubscriberEx ts = pp + .doOnCancel(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestSubscriberEx<>(0L)); + + ts.assertEmpty(); + + pp.onNext(1); + + ts.assertFailure(CompositeException.class); + + TestHelper.assertCompositeExceptions(ts, + MissingBackpressureException.class, "Could not emit value due to lack of requests", + TestException.class, "forced 1"); + + verify(whenDisposed).run(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeIntervalTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeIntervalTest.java index ac517661cb..e2e9550388 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeIntervalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeIntervalTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -61,11 +61,11 @@ public void timeInterval() { processor.onComplete(); inOrder.verify(subscriber, times(1)).onNext( - new Timed(1, 1000, TIME_UNIT)); + new Timed<>(1, 1000, TIME_UNIT)); inOrder.verify(subscriber, times(1)).onNext( - new Timed(2, 2000, TIME_UNIT)); + new Timed<>(2, 2000, TIME_UNIT)); inOrder.verify(subscriber, times(1)).onNext( - new Timed(3, 3000, TIME_UNIT)); + new Timed<>(3, 3000, TIME_UNIT)); inOrder.verify(subscriber, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @@ -129,7 +129,6 @@ public void dispose() { TestHelper.checkDisposed(Flowable.just(1).timeInterval()); } - @SuppressWarnings("unchecked") @Test public void error() { Flowable.error(new TestException()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java index 144d0da6a8..070bb620b0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.schedulers.TestScheduler; +import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -52,7 +52,7 @@ public void setUp() { @Test public void shouldNotTimeoutIfOnNextWithinTimeout() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); withTimeout.subscribe(ts); @@ -67,7 +67,7 @@ public void shouldNotTimeoutIfOnNextWithinTimeout() { @Test public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); withTimeout.subscribe(ts); @@ -83,7 +83,7 @@ public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { @Test public void shouldTimeoutIfOnNextNotWithinTimeout() { - TestSubscriberEx subscriber = new TestSubscriberEx(); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); withTimeout.subscribe(subscriber); @@ -93,8 +93,8 @@ public void shouldTimeoutIfOnNextNotWithinTimeout() { @Test public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { - TestSubscriberEx subscriber = new TestSubscriberEx(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); + TestSubscriber ts = new TestSubscriber<>(subscriber); withTimeout.subscribe(subscriber); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); underlyingSubject.onNext("One"); @@ -107,7 +107,7 @@ public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { @Test public void shouldCompleteIfUnderlyingComletes() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); withTimeout.subscribe(subscriber); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); underlyingSubject.onComplete(); @@ -120,7 +120,7 @@ public void shouldCompleteIfUnderlyingComletes() { @Test public void shouldErrorIfUnderlyingErrors() { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); withTimeout.subscribe(subscriber); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); underlyingSubject.onError(new UnsupportedOperationException()); @@ -135,7 +135,7 @@ public void shouldSwitchToOtherIfOnNextNotWithinTimeout() { Flowable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); source.subscribe(ts); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -158,7 +158,7 @@ public void shouldSwitchToOtherIfOnErrorNotWithinTimeout() { Flowable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); source.subscribe(ts); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -181,7 +181,7 @@ public void shouldSwitchToOtherIfOnCompletedNotWithinTimeout() { Flowable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); source.subscribe(ts); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -204,7 +204,7 @@ public void shouldSwitchToOtherAndCanBeUnsubscribedIfOnNextNotWithinTimeout() { Flowable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); source.subscribe(ts); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -234,7 +234,7 @@ public void shouldTimeoutIfSynchronizedFlowableEmitFirstOnNextNotWithinTimeout() final CountDownLatch exit = new CountDownLatch(1); final CountDownLatch timeoutSetuped = new CountDownLatch(1); - final TestSubscriberEx subscriber = new TestSubscriberEx(); + final TestSubscriberEx subscriber = new TestSubscriberEx<>(); new Thread(new Runnable() { @@ -283,7 +283,7 @@ public void subscribe(Subscriber subscriber) { TestScheduler testScheduler = new TestScheduler(); Flowable observableWithTimeout = never.timeout(1000, TimeUnit.MILLISECONDS, testScheduler); - TestSubscriberEx subscriber = new TestSubscriberEx(); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); observableWithTimeout.subscribe(subscriber); testScheduler.advanceTimeBy(2000, TimeUnit.MILLISECONDS); @@ -528,4 +528,9 @@ public void run() { } } } + + @Test + public void doubleOnSubscribeFallback() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.timeout(1, TimeUnit.MINUTES, Flowable.never())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java index ff0b91113a..538a8e566a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimeoutWithSelectorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,6 +32,7 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.operators.flowable.FlowableTimeout.TimeoutConsumer; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; @@ -329,7 +330,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { }).when(subscriber).onComplete(); - final TestSubscriber ts = new TestSubscriber(subscriber); + final TestSubscriber ts = new TestSubscriber<>(subscriber); new Thread(new Runnable() { @@ -894,4 +895,13 @@ protected void subscribeActual(Subscriber s) { RxJavaPlugins.reset(); } } + + @Test + public void timeoutConsumerIsDisposed() { + TimeoutConsumer consumer = new TimeoutConsumer(0, null); + + assertFalse(consumer.isDisposed()); + consumer.dispose(); + assertTrue(consumer.isDisposed()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java index bc8b67d737..4f0216e612 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -63,7 +63,7 @@ public void timerOnce() { @Test public void timerPeriodically() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.interval(100, 100, TimeUnit.MILLISECONDS, scheduler).subscribe(ts); @@ -91,7 +91,7 @@ public void timerPeriodically() { @Test public void interval() { Flowable w = Flowable.interval(1, TimeUnit.SECONDS, scheduler); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); w.subscribe(ts); ts.assertNoValues(); @@ -116,8 +116,8 @@ public void interval() { public void withMultipleSubscribersStartingAtSameTime() { Flowable w = Flowable.interval(1, TimeUnit.SECONDS, scheduler); - TestSubscriber ts1 = new TestSubscriber(); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber<>(); w.subscribe(ts1); w.subscribe(ts2); @@ -153,7 +153,7 @@ public void withMultipleSubscribersStartingAtSameTime() { public void withMultipleStaggeredSubscribers() { Flowable w = Flowable.interval(1, TimeUnit.SECONDS, scheduler); - TestSubscriber ts1 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); w.subscribe(ts1); @@ -161,7 +161,7 @@ public void withMultipleStaggeredSubscribers() { scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts2 = new TestSubscriber<>(); w.subscribe(ts2); @@ -193,7 +193,7 @@ public void withMultipleStaggeredSubscribers() { public void withMultipleStaggeredSubscribersAndPublish() { ConnectableFlowable w = Flowable.interval(1, TimeUnit.SECONDS, scheduler).publish(); - TestSubscriber ts1 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); w.subscribe(ts1); w.connect(); @@ -202,7 +202,7 @@ public void withMultipleStaggeredSubscribersAndPublish() { scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - TestSubscriber ts2 = new TestSubscriber(); + TestSubscriber ts2 = new TestSubscriber<>(); w.subscribe(ts2); ts1.assertValues(0L, 1L); @@ -309,7 +309,7 @@ public void backpressureNotReady() { @Test public void timerCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final TestScheduler scheduler = new TestScheduler(); @@ -352,7 +352,7 @@ public void timerDelayZero() { public void timerInterruptible() throws Exception { ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); try { - for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec) }) { + for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec, true) }) { final AtomicBoolean interrupted = new AtomicBoolean(); TestSubscriber ts = Flowable.timer(1, TimeUnit.MILLISECONDS, s) .map(new Function() { @@ -380,4 +380,9 @@ public Long apply(Long v) throws Exception { exec.shutdown(); } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.timer(1, TimeUnit.MINUTES)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimestampTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimestampTest.java index 08bbcdc50f..6d98c0c414 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimestampTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableTimestampTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,9 +53,9 @@ public void timestampWithScheduler() { InOrder inOrder = inOrder(subscriber); - inOrder.verify(subscriber, times(1)).onNext(new Timed(1, 0, TimeUnit.MILLISECONDS)); - inOrder.verify(subscriber, times(1)).onNext(new Timed(2, 100, TimeUnit.MILLISECONDS)); - inOrder.verify(subscriber, times(1)).onNext(new Timed(3, 200, TimeUnit.MILLISECONDS)); + inOrder.verify(subscriber, times(1)).onNext(new Timed<>(1, 0, TimeUnit.MILLISECONDS)); + inOrder.verify(subscriber, times(1)).onNext(new Timed<>(2, 100, TimeUnit.MILLISECONDS)); + inOrder.verify(subscriber, times(1)).onNext(new Timed<>(3, 200, TimeUnit.MILLISECONDS)); verify(subscriber, never()).onError(any(Throwable.class)); verify(subscriber, never()).onComplete(); @@ -77,9 +77,9 @@ public void timestampWithScheduler2() { InOrder inOrder = inOrder(subscriber); - inOrder.verify(subscriber, times(1)).onNext(new Timed(1, 0, TimeUnit.MILLISECONDS)); - inOrder.verify(subscriber, times(1)).onNext(new Timed(2, 0, TimeUnit.MILLISECONDS)); - inOrder.verify(subscriber, times(1)).onNext(new Timed(3, 200, TimeUnit.MILLISECONDS)); + inOrder.verify(subscriber, times(1)).onNext(new Timed<>(1, 0, TimeUnit.MILLISECONDS)); + inOrder.verify(subscriber, times(1)).onNext(new Timed<>(2, 0, TimeUnit.MILLISECONDS)); + inOrder.verify(subscriber, times(1)).onNext(new Timed<>(3, 200, TimeUnit.MILLISECONDS)); verify(subscriber, never()).onError(any(Throwable.class)); verify(subscriber, never()).onComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToCompletableTest.java index 84c52ab2c3..dff5a7462b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToCompletableTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.flowable; import static org.junit.Assert.assertFalse; @@ -73,7 +71,7 @@ public void emptyObservable() { @Test public void neverObservable() { - TestSubscriberEx subscriber = new TestSubscriberEx(); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); Completable cmp = Flowable.never().ignoreElements(); cmp.toFlowable().subscribe(subscriber); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToFutureTest.java index 28a61aa223..8501a22e74 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToFutureTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToFutureTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ public void success() throws Exception { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); Flowable.fromFuture(future).subscribe(ts); @@ -60,9 +60,9 @@ public void successOperatesOnSuppliedScheduler() throws Exception { Subscriber subscriber = TestHelper.mockSubscriber(); TestScheduler scheduler = new TestScheduler(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); - Flowable.fromFuture(future, scheduler).subscribe(ts); + Flowable.fromFuture(future).subscribeOn(scheduler).subscribe(ts); verify(subscriber, never()).onNext(value); @@ -80,7 +80,7 @@ public void failure() throws Exception { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); Flowable.fromFuture(future).subscribe(ts); @@ -101,7 +101,7 @@ public void cancelledBeforeSubscribe() throws Exception { Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); ts.cancel(); Flowable.fromFuture(future).subscribe(ts); @@ -147,7 +147,7 @@ public Object get(long timeout, TimeUnit unit) throws InterruptedException, Exec Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); Flowable futureObservable = Flowable.fromFuture(future); futureObservable.subscribeOn(Schedulers.computation()).subscribe(ts); @@ -163,9 +163,9 @@ public Object get(long timeout, TimeUnit unit) throws InterruptedException, Exec @Test public void backpressure() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); - FutureTask f = new FutureTask(new Runnable() { + FutureTask f = new FutureTask<>(new Runnable() { @Override public void run() { @@ -187,7 +187,7 @@ public void run() { @Test public void withTimeoutNoTimeout() { - FutureTask task = new FutureTask(new Runnable() { + FutureTask task = new FutureTask<>(new Runnable() { @Override public void run() { @@ -207,7 +207,7 @@ public void run() { @Test public void withTimeoutTimeout() { - FutureTask task = new FutureTask(new Runnable() { + FutureTask task = new FutureTask<>(new Runnable() { @Override public void run() { @@ -225,7 +225,7 @@ public void run() { @Test public void withTimeoutNoTimeoutScheduler() { - FutureTask task = new FutureTask(new Runnable() { + FutureTask task = new FutureTask<>(new Runnable() { @Override public void run() { @@ -234,7 +234,7 @@ public void run() { TestSubscriber ts = TestSubscriber.create(); - Flowable.fromFuture(task, Schedulers.computation()).subscribe(ts); + Flowable.fromFuture(task).subscribeOn(Schedulers.computation()).subscribe(ts); task.run(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListTest.java index 66d2164b70..208f371bb7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToListTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -92,7 +92,7 @@ public void listWithBlockingFirstFlowable() { @Test public void backpressureHonoredFlowable() { Flowable> w = Flowable.just(1, 2, 3, 4, 5).toList().toFlowable(); - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); w.subscribe(ts); @@ -113,7 +113,6 @@ public void backpressureHonoredFlowable() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void capacityHintFlowable() { Flowable.range(1, 10) @@ -182,7 +181,6 @@ static void await(CyclicBarrier cb) { } } - @SuppressWarnings("unchecked") @Test public void capacityHint() { Flowable.range(1, 10) @@ -198,7 +196,6 @@ public void dispose() { TestHelper.checkDisposed(Flowable.just(1).toList()); } - @SuppressWarnings("unchecked") @Test public void error() { Flowable.error(new TestException()) @@ -208,7 +205,6 @@ public void error() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorSingle() { Flowable.error(new TestException()) @@ -217,7 +213,6 @@ public void errorSingle() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void collectionSupplierThrows() { Flowable.just(1) @@ -232,7 +227,6 @@ public Collection get() throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void collectionSupplierReturnsNull() { Flowable.just(1) @@ -248,7 +242,6 @@ public Collection get() throws Exception { .assertErrorMessage(ExceptionHelper.nullWarning("The collectionSupplier returned a null Collection.")); } - @SuppressWarnings("unchecked") @Test public void singleCollectionSupplierThrows() { Flowable.just(1) @@ -262,7 +255,6 @@ public Collection get() throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void singleCollectionSupplierReturnsNull() { Flowable.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMapTest.java index 8635158cc3..736cf4caa5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -54,7 +54,7 @@ public void toMapFlowable() { Flowable> mapped = source.toMap(lengthFunc).toFlowable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -73,7 +73,7 @@ public void toMapWithValueSelectorFlowable() { Flowable> mapped = source.toMap(lengthFunc, duplicate).toFlowable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -101,7 +101,7 @@ public Integer apply(String t1) { }; Flowable> mapped = source.toMap(lengthFuncErr).toFlowable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -131,7 +131,7 @@ public String apply(String t1) { Flowable> mapped = source.toMap(lengthFunc, duplicateErr).toFlowable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -177,7 +177,7 @@ public String apply(String v) { } }, mapFactory).toFlowable(); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); @@ -213,7 +213,7 @@ public String apply(String v) { } }, mapFactory).toFlowable(); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); @@ -231,7 +231,7 @@ public void toMap() { Single> mapped = source.toMap(lengthFunc); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -249,7 +249,7 @@ public void toMapWithValueSelector() { Single> mapped = source.toMap(lengthFunc, duplicate); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -276,7 +276,7 @@ public Integer apply(String t1) { }; Single> mapped = source.toMap(lengthFuncErr); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -305,7 +305,7 @@ public String apply(String t1) { Single> mapped = source.toMap(lengthFunc, duplicateErr); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -350,7 +350,7 @@ public String apply(String v) { } }, mapFactory); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); @@ -385,7 +385,7 @@ public String apply(String v) { } }, mapFactory); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMultimapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMultimapTest.java index 2413ed6afe..63db446ee6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMultimapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToMultimapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -55,7 +55,7 @@ public void toMultimapFlowable() { Flowable>> mapped = source.toMultimap(lengthFunc).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -72,7 +72,7 @@ public void toMultimapWithValueSelectorFlowable() { Flowable>> mapped = source.toMultimap(lengthFunc, duplicate).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -114,11 +114,11 @@ public String apply(String v) { mapFactory, new Function>() { @Override public Collection apply(Integer e) { - return new ArrayList(); + return new ArrayList<>(); } }).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -137,9 +137,9 @@ public void toMultimapWithCollectionFactoryFlowable() { @Override public Collection apply(Integer t1) { if (t1 == 2) { - return new ArrayList(); + return new ArrayList<>(); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -153,16 +153,16 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Flowable>> mapped = source .toMultimap(lengthFunc, identity, mapSupplier, collectionFactory).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); - expected.put(3, new HashSet(Arrays.asList("eee"))); + expected.put(3, new HashSet<>(Arrays.asList("eee"))); mapped.subscribe(objectSubscriber); @@ -187,7 +187,7 @@ public Integer apply(String t1) { Flowable>> mapped = source.toMultimap(lengthFuncErr).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -214,7 +214,7 @@ public String apply(String t1) { Flowable>> mapped = source.toMultimap(lengthFunc, duplicateErr).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -244,7 +244,7 @@ public String apply(String v) { } }, mapFactory).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -265,7 +265,7 @@ public Collection apply(Integer t1) { if (t1 == 2) { throw new RuntimeException("Forced failure"); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -279,14 +279,14 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Flowable>> mapped = source.toMultimap(lengthFunc, identity, mapSupplier, collectionFactory).toFlowable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Collections.singleton("eee")); @@ -303,7 +303,7 @@ public void toMultimap() { Single>> mapped = source.toMultimap(lengthFunc); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -319,7 +319,7 @@ public void toMultimapWithValueSelector() { Single>> mapped = source.toMultimap(lengthFunc, duplicate); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -360,11 +360,11 @@ public String apply(String v) { mapFactory, new Function>() { @Override public Collection apply(Integer e) { - return new ArrayList(); + return new ArrayList<>(); } }); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -382,9 +382,9 @@ public void toMultimapWithCollectionFactory() { @Override public Collection apply(Integer t1) { if (t1 == 2) { - return new ArrayList(); + return new ArrayList<>(); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -398,16 +398,16 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Single>> mapped = source .toMultimap(lengthFunc, identity, mapSupplier, collectionFactory); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); - expected.put(3, new HashSet(Arrays.asList("eee"))); + expected.put(3, new HashSet<>(Arrays.asList("eee"))); mapped.subscribe(singleObserver); @@ -431,7 +431,7 @@ public Integer apply(String t1) { Single>> mapped = source.toMultimap(lengthFuncErr); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -457,7 +457,7 @@ public String apply(String t1) { Single>> mapped = source.toMultimap(lengthFunc, duplicateErr); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -486,7 +486,7 @@ public String apply(String v) { } }, mapFactory); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -506,7 +506,7 @@ public Collection apply(Integer t1) { if (t1 == 2) { throw new RuntimeException("Forced failure"); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -520,14 +520,14 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Single>> mapped = source.toMultimap(lengthFunc, identity, mapSupplier, collectionFactory); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Collections.singleton("eee")); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSingleTest.java index b88824b583..d064589b1d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSortedListTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSortedListTest.java index 96ec70ef69..cadcb572b6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSortedListTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableToSortedListTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -72,7 +72,7 @@ public void withFollowingFirstFlowable() { @Test public void backpressureHonoredFlowable() { Flowable> w = Flowable.just(1, 3, 2, 5, 4).toSortedList().toFlowable(); - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); w.subscribe(ts); @@ -112,7 +112,6 @@ public int compare(Integer a, Integer b) { .assertResult(5, 4, 3, 2, 1); } - @SuppressWarnings("unchecked") @Test public void toSortedListCapacityFlowable() { Flowable.just(5, 1, 2, 4, 3).toSortedList(4).toFlowable() @@ -120,7 +119,6 @@ public void toSortedListCapacityFlowable() { .assertResult(Arrays.asList(1, 2, 3, 4, 5)); } - @SuppressWarnings("unchecked") @Test public void toSortedListComparatorCapacityFlowable() { Flowable.just(5, 1, 2, 4, 3).toSortedList(new Comparator() { @@ -178,7 +176,6 @@ static void await(CyclicBarrier cb) { } } - @SuppressWarnings("unchecked") @Test public void toSortedListCapacity() { Flowable.just(5, 1, 2, 4, 3).toSortedList(4) @@ -186,7 +183,6 @@ public void toSortedListCapacity() { .assertResult(Arrays.asList(1, 2, 3, 4, 5)); } - @SuppressWarnings("unchecked") @Test public void toSortedListComparatorCapacity() { Flowable.just(5, 1, 2, 4, 3).toSortedList(new Comparator() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java index 6457f5ae26..2a8efa4593 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUnsubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,6 +26,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -38,7 +39,7 @@ public void unsubscribeWhenSubscribeOnAndUnsubscribeOnAreOnSameThread() throws I UIEventLoopScheduler uiEventLoop = new UIEventLoopScheduler(); try { final ThreadSubscription subscription = new ThreadSubscription(); - final AtomicReference subscribeThread = new AtomicReference(); + final AtomicReference subscribeThread = new AtomicReference<>(); Flowable w = Flowable.unsafeCreate(new Publisher() { @Override @@ -54,7 +55,7 @@ public void subscribe(Subscriber t1) { } }); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); w.subscribeOn(uiEventLoop).observeOn(Schedulers.computation()) .unsubscribeOn(uiEventLoop) .take(2) @@ -87,7 +88,7 @@ public void unsubscribeWhenSubscribeOnAndUnsubscribeOnAreOnDifferentThreads() th UIEventLoopScheduler uiEventLoop = new UIEventLoopScheduler(); try { final ThreadSubscription subscription = new ThreadSubscription(); - final AtomicReference subscribeThread = new AtomicReference(); + final AtomicReference subscribeThread = new AtomicReference<>(); Flowable w = Flowable.unsafeCreate(new Publisher() { @Override @@ -103,7 +104,7 @@ public void subscribe(Subscriber t1) { } }); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); w.subscribeOn(Schedulers.newThread()).observeOn(Schedulers.computation()) .unsubscribeOn(uiEventLoop) .take(2) @@ -274,4 +275,9 @@ protected void subscribeActual(Subscriber subscriber) { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.unsubscribeOn(ImmediateThinScheduler.INSTANCE)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsingTest.java index 0422a24db2..d744c32747 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableUsingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -210,7 +210,7 @@ private void performTestUsingWithFlowableFactoryError(boolean disposeEagerly) { Supplier resourceFactory = new Supplier() { @Override public Disposable get() { - return Disposables.fromRunnable(unsubscribe); + return Disposable.fromRunnable(unsubscribe); } }; @@ -233,7 +233,7 @@ public Flowable apply(Disposable subscription) { @Test public void usingDisposesEagerlyBeforeCompletion() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); Supplier resourceFactory = createResourceFactory(events); final Action completion = createOnCompletedAction(events); final Action unsub = createUnsubAction(events); @@ -260,7 +260,7 @@ public Flowable apply(Resource resource) { @Test public void usingDoesNotDisposesEagerlyBeforeCompletion() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); Supplier resourceFactory = createResourceFactory(events); final Action completion = createOnCompletedAction(events); final Action unsub = createUnsubAction(events); @@ -287,7 +287,7 @@ public Flowable apply(Resource resource) { @Test public void usingDisposesEagerlyBeforeError() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); Supplier resourceFactory = createResourceFactory(events); final Consumer onError = createOnErrorAction(events); final Action unsub = createUnsubAction(events); @@ -315,7 +315,7 @@ public Flowable apply(Resource resource) { @Test public void usingDoesNotDisposesEagerlyBeforeError() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); final Supplier resourceFactory = createResourceFactory(events); final Consumer onError = createOnErrorAction(events); final Action unsub = createUnsubAction(events); @@ -604,7 +604,7 @@ public Flowable apply(Flowable f) @Test public void eagerDisposedOnComplete() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.using(Functions.justSupplier(1), Functions.justFunction(new Flowable() { @Override @@ -619,7 +619,7 @@ protected void subscribeActual(Subscriber subscriber) { @Test public void eagerDisposedOnError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.using(Functions.justSupplier(1), Functions.justFunction(new Flowable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java index fcd2390f7e..5f5d97af1e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -43,7 +43,7 @@ public void windowViaFlowableNormal1() { final Subscriber subscriber = TestHelper.mockSubscriber(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Subscriber> wo = new DefaultSubscriber>() { @Override @@ -100,7 +100,7 @@ public void windowViaFlowableBoundaryCompletes() { final Subscriber subscriber = TestHelper.mockSubscriber(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Subscriber> wo = new DefaultSubscriber>() { @Override @@ -156,7 +156,7 @@ public void windowViaFlowableBoundaryThrows() { final Subscriber subscriber = TestHelper.mockSubscriber(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Subscriber> wo = new DefaultSubscriber>() { @Override @@ -206,7 +206,7 @@ public void windowViaFlowableThrows() { final Subscriber subscriber = TestHelper.mockSubscriber(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Subscriber> wo = new DefaultSubscriber>() { @Override @@ -328,7 +328,6 @@ public Flowable apply(Flowable v) throws Exception { }, false, 1, 1, 1); } - @SuppressWarnings("unchecked") @Test public void boundaryDirectMissingBackpressure() { List errors = TestHelper.trackPluginErrors(); @@ -344,7 +343,6 @@ public void boundaryDirectMissingBackpressure() { } } - @SuppressWarnings("unchecked") @Test public void boundaryDirectMissingBackpressureNoNullPointerException() { List errors = TestHelper.trackPluginErrors(); @@ -408,7 +406,7 @@ public Flowable apply( public void mainAndBoundaryBothError() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); TestSubscriberEx> ts = Flowable.error(new TestException("main")) .window(new Flowable() { @@ -418,6 +416,12 @@ protected void subscribeActual(Subscriber subscriber) { ref.set(subscriber); } }) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) .to(TestHelper.>testConsumer()); ts @@ -441,8 +445,8 @@ public void mainCompleteBoundaryErrorRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); TestSubscriberEx> ts = new Flowable() { @Override @@ -491,8 +495,8 @@ public void run() { @Test public void mainNextBoundaryNextRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); TestSubscriber> ts = new Flowable() { @Override @@ -534,8 +538,8 @@ public void run() { @Test public void takeOneAnotherBoundary() { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); TestSubscriberEx> ts = new Flowable() { @Override @@ -566,8 +570,8 @@ protected void subscribeActual(Subscriber subscriber) { @Test public void disposeMainBoundaryCompleteRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); final TestSubscriber> ts = new Flowable() { @Override @@ -619,12 +623,13 @@ public void run() { } @Test + @SuppressUndeliverable public void disposeMainBoundaryErrorRace() { final TestException ex = new TestException(); for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); final TestSubscriber> ts = new Flowable() { @Override @@ -674,4 +679,65 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void cancellingWindowCancelsUpstream() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.window(Flowable.just(1).concatWith(Flowable.never())) + .take(1) + .flatMap(new Function, Publisher>() { + @Override + public Publisher apply(Flowable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts + .assertResult(1); + + assertFalse("Processor still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void windowAbandonmentCancelsUpstream() { + PublishProcessor pp = PublishProcessor.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestSubscriber> ts = pp.window(Flowable.never()) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts + .assertValueCount(1) + ; + + pp.onNext(1); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + ts + .assertValueCount(1) + .assertNoErrors() + .assertNotComplete(); + + assertFalse("Processor still has subscribers!", pp.hasSubscribers()); + + inner.get().test().assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java index 4536d7a8e4..bd50a20a2a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithSizeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,6 +25,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; @@ -99,7 +100,7 @@ public void skipAndCountWindowsWithGaps() { @Test public void windowUnsubscribeNonOverlapping() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final AtomicInteger count = new AtomicInteger(); Flowable.merge(Flowable.range(1, 10000).doOnNext(new Consumer() { @@ -121,7 +122,7 @@ public void accept(Integer t1) { @Test public void windowUnsubscribeNonOverlappingAsyncSource() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final AtomicInteger count = new AtomicInteger(); Flowable.merge(Flowable.range(1, 100000) .doOnNext(new Consumer() { @@ -145,7 +146,7 @@ public void accept(Integer t1) { @Test public void windowUnsubscribeOverlapping() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final AtomicInteger count = new AtomicInteger(); Flowable.merge(Flowable.range(1, 10000).doOnNext(new Consumer() { @@ -164,7 +165,7 @@ public void accept(Integer t1) { @Test public void windowUnsubscribeOverlappingAsyncSource() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final AtomicInteger count = new AtomicInteger(); Flowable.merge(Flowable.range(1, 100000) .doOnNext(new Consumer() { @@ -187,7 +188,7 @@ public void accept(Integer t1) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -198,7 +199,7 @@ private List list(String... args) { public void backpressureOuter() { Flowable> source = Flowable.range(1, 10).window(3); - final List list = new ArrayList(); + final List list = new ArrayList<>(); final Subscriber subscriber = TestHelper.mockSubscriber(); @@ -271,7 +272,7 @@ public void subscribe(Subscriber s) { @Test public void takeFlatMapCompletes() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final int indicator = 999999999; @@ -290,10 +291,9 @@ public Flowable apply(Flowable w) { ts.assertValueCount(22); } - @SuppressWarnings("unchecked") @Test public void backpressureOuterInexact() { - TestSubscriber> ts = new TestSubscriber>(0L); + TestSubscriber> ts = new TestSubscriber<>(0L); Flowable.range(1, 5) .window(2, 1) @@ -364,7 +364,6 @@ public Flowable> apply(Flowable f) throws Exception { }); } - @SuppressWarnings("unchecked") @Test public void errorExact() { Flowable.error(new TestException()) @@ -373,7 +372,6 @@ public void errorExact() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorSkip() { Flowable.error(new TestException()) @@ -382,7 +380,6 @@ public void errorSkip() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorOverlap() { Flowable.error(new TestException()) @@ -476,7 +473,7 @@ public Publisher apply(Flowable w) throws Throwable { public void windowAbandonmentCancelsUpstreamSize() { PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); TestSubscriber> ts = pp.window(10) .take(1) @@ -530,7 +527,7 @@ public Publisher apply(Flowable w) throws Throwable { public void windowAbandonmentCancelsUpstreamSkip() { PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); TestSubscriber> ts = pp.window(5, 10) .take(1) @@ -584,7 +581,7 @@ public Publisher apply(Flowable w) throws Throwable { public void windowAbandonmentCancelsUpstreamOverlap() { PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); TestSubscriber> ts = pp.window(5, 3) .take(1) @@ -609,4 +606,210 @@ public void accept(Flowable v) throws Throwable { inner.get().test().assertResult(1); } + + @Test + public void badRequestExact() { + TestHelper.assertBadRequestReported(Flowable.never().window(1)); + } + + @Test + public void badRequestSkip() { + TestHelper.assertBadRequestReported(Flowable.never().window(1, 2)); + } + + @Test + public void badRequestOverlap() { + TestHelper.assertBadRequestReported(Flowable.never().window(2, 1)); + } + + @Test + public void skipEmpty() { + Flowable.empty() + .window(1, 2) + .test() + .assertResult(); + } + + @Test + public void exactEmpty() { + Flowable.empty() + .window(2) + .test() + .assertResult(); + } + + @Test + public void skipMultipleRequests() { + Flowable.range(1, 10) + .window(1, 2) + .doOnNext(w -> w.test()) + .rebatchRequests(1) + .test() + .assertComplete(); + } + + @Test + public void skipOne() { + Flowable.just(1) + .window(2, 3) + .flatMap(v -> v) + .test() + .assertResult(1); + } + + @Test + public void overlapMultipleRequests() { + Flowable.range(1, 10) + .window(2, 1) + .doOnNext(w -> w.test()) + .rebatchRequests(1) + .test() + .assertComplete(); + } + + @Test + public void overlapCancelAfterWindow() { + Flowable.range(1, 10) + .window(2, 1) + .takeUntil(v -> true) + .doOnNext(w -> w.test()) + .test(0L) + .requestMore(10) + .assertComplete(); + } + + @Test + public void overlapEmpty() { + Flowable.empty() + .window(2, 1) + .test() + .assertResult(); + } + + @Test + public void overlapEmptyNoRequest() { + Flowable.empty() + .window(2, 1) + .test(0L) + .assertResult(); + } + + @Test + public void overlapMoreWorkAfterOnNext() { + PublishProcessor pp = PublishProcessor.create(); + AtomicBoolean once = new AtomicBoolean(); + + TestSubscriber> ts = pp.window(2, 1) + .doOnNext(v -> { + v.test(); + if (once.compareAndSet(false, true)) { + pp.onNext(2); + pp.onComplete(); + } + }) + .test(); + + pp.onNext(1); + + ts.assertComplete(); + } + + @Test + public void moreQueuedClean() { + Flowable.range(1, 10) + .window(5, 1) + .doOnNext(w -> w.test()) + .test(3) + .cancel(); + } + + @Test + public void cancelWithoutWindowSize() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelAfterAbandonmentSize() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelWithoutWindowSkip() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 15) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelAfterAbandonmentSkip() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 15) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelWithoutWindowOverlap() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 5) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void cancelAfterAbandonmentOverlap() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(10, 5) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts.cancel(); + + assertFalse("Subject still has subscribers!", pp.hasSubscribers()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java index 3a76a4c7a8..f2431eb8db 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithStartEndFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,9 +15,10 @@ import static org.junit.Assert.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.*; import org.junit.*; import org.reactivestreams.*; @@ -31,7 +32,7 @@ import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableWindowWithStartEndFlowableTest extends RxJavaTest { @@ -46,8 +47,8 @@ public void before() { @Test public void flowableBasedOpenerAndCloser() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); Flowable source = Flowable.unsafeCreate(new Publisher() { @Override @@ -96,7 +97,7 @@ public void subscribe(Subscriber subscriber) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -128,7 +129,7 @@ public void accept(Flowable stringFlowable) { stringFlowable.subscribe(new DefaultSubscriber() { @Override public void onComplete() { - lists.add(new ArrayList(list)); + lists.add(new ArrayList<>(list)); list.clear(); } @@ -153,14 +154,21 @@ public void noUnsubscribeAndNoLeak() { PublishProcessor open = PublishProcessor.create(); final PublishProcessor close = PublishProcessor.create(); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); source.window(open, new Function>() { @Override public Flowable apply(Integer t) { return close; } - }).subscribe(ts); + }) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) + .subscribe(ts); open.onNext(1); source.onNext(1); @@ -190,14 +198,21 @@ public void unsubscribeAll() { PublishProcessor open = PublishProcessor.create(); final PublishProcessor close = PublishProcessor.create(); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); source.window(open, new Function>() { @Override public Flowable apply(Integer t) { return close; } - }).subscribe(ts); + }) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) + .subscribe(ts); open.onNext(1); @@ -219,20 +234,20 @@ public void dispose() { @Test public void reentrant() { - final FlowableProcessor ps = PublishProcessor.create(); + final FlowableProcessor pp = PublishProcessor.create(); TestSubscriber ts = new TestSubscriber() { @Override public void onNext(Integer t) { super.onNext(t); if (t == 1) { - ps.onNext(2); - ps.onComplete(); + pp.onNext(2); + pp.onComplete(); } } }; - ps.window(BehaviorProcessor.createDefault(1), Functions.justFunction(Flowable.never())) + pp.window(BehaviorProcessor.createDefault(1), Functions.justFunction(Flowable.never())) .flatMap(new Function, Flowable>() { @Override public Flowable apply(Flowable v) throws Exception { @@ -241,23 +256,13 @@ public Flowable apply(Flowable v) throws Exception { }) .subscribe(ts); - ps.onNext(1); + pp.onNext(1); ts .awaitDone(1, TimeUnit.SECONDS) .assertResult(1, 2); } - @Test - public void badSourceCallable() { - TestHelper.checkBadSourceFlowable(new Function, Object>() { - @Override - public Object apply(Flowable f) throws Exception { - return f.window(Flowable.just(1), Functions.justFunction(Flowable.never())); - } - }, false, 1, 1, (Object[])null); - } - @Test public void boundarySelectorNormal() { PublishProcessor source = PublishProcessor.create(); @@ -319,6 +324,7 @@ public Flowable apply(Integer v) throws Exception { } @Test + @SuppressUndeliverable public void endError() { PublishProcessor source = PublishProcessor.create(); PublishProcessor start = PublishProcessor.create(); @@ -372,6 +378,12 @@ protected void subscribeActual( }; } }) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) .test() .assertValueCount(1) .assertNoErrors() @@ -406,6 +418,12 @@ public Flowable apply(Integer v) throws Exception { return flowableDisposed(closeDisposed); } }) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) .to(TestHelper.>testConsumer()) .assertSubscribed() .assertNoErrors() @@ -418,7 +436,6 @@ public Flowable apply(Integer v) throws Exception { } @Test - @SuppressWarnings("unchecked") public void mainWindowMissingBackpressure() { PublishProcessor source = PublishProcessor.create(); PublishProcessor boundary = PublishProcessor.create(); @@ -436,4 +453,267 @@ public void mainWindowMissingBackpressure() { assertFalse(source.hasSubscribers()); assertFalse(boundary.hasSubscribers()); } + + @Test + public void cancellingWindowCancelsUpstream() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.window(Flowable.just(1).concatWith(Flowable.never()), Functions.justFunction(Flowable.never())) + .take(1) + .flatMap(new Function, Publisher>() { + @Override + public Publisher apply(Flowable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(pp.hasSubscribers()); + + pp.onNext(1); + + ts + .assertResult(1); + + assertFalse("Processor still has subscribers!", pp.hasSubscribers()); + } + + @Test + public void windowAbandonmentCancelsUpstream() { + PublishProcessor pp = PublishProcessor.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestSubscriber> ts = pp.window(Flowable.just(1).concatWith(Flowable.never()), + Functions.justFunction(Flowable.never())) + .doOnNext(new Consumer>() { + @Override + public void accept(Flowable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(pp.hasSubscribers()); + + ts + .assertValueCount(1) + ; + + pp.onNext(1); + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + ts + .assertValueCount(1) + .assertNoErrors() + .assertNotComplete(); + + assertFalse("Processor still has subscribers!", pp.hasSubscribers()); + + inner.get().test().assertResult(); + } + + @Test + public void closingIndicatorFunctionCrash() { + + PublishProcessor source = PublishProcessor.create(); + PublishProcessor boundary = PublishProcessor.create(); + + TestSubscriber> ts = source.window(boundary, new Function>() { + @Override + public Publisher apply(Integer end) throws Throwable { + throw new TestException(); + } + }) + .test() + ; + + ts.assertEmpty(); + + boundary.onNext(1); + + ts.assertFailure(TestException.class); + + assertFalse(source.hasSubscribers()); + assertFalse(boundary.hasSubscribers()); + + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(o -> o.window(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void openError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex1 = new TestException(); + TestException ex2 = new TestException(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = Flowable.fromPublisher(ref1::set); + Flowable f2 = Flowable.fromPublisher(ref2::set); + + TestSubscriber> ts = BehaviorProcessor.createDefault(1) + .window(f1, v -> f2) + .doOnNext(w -> w.test()) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref2.get().onSubscribe(new BooleanSubscription()); + + TestHelper.race( + () -> ref1.get().onError(ex1), + () -> ref2.get().onError(ex2) + ); + + ts.assertError(RuntimeException.class); + + if (!errors.isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + + errors.clear(); + } + }); + } + + @Test + public void closeError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = Flowable.unsafeCreate(ref1::set); + Flowable f2 = Flowable.unsafeCreate(ref2::set); + + TestSubscriber ts = BehaviorProcessor.createDefault(1) + .window(f1, v -> f2) + .flatMap(v -> v) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref2.get().onSubscribe(new BooleanSubscription()); + + ref2.get().onError(new TestException()); + ref2.get().onError(new TestException()); + + ts.assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void upstreamFailsBeforeFirstWindow() { + Flowable.error(new TestException()) + .window(Flowable.never(), v -> Flowable.never()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void windowOpenMainCompletes() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishProcessor pp = PublishProcessor.create(); + Flowable f1 = Flowable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber> ts = pp + .window(f1, v -> Flowable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + pp.onNext(1); + pp.onComplete(); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + + ts.assertComplete(); + } + + @Test + public void windowOpenMainError() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishProcessor pp = PublishProcessor.create(); + Flowable f1 = Flowable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestSubscriber> ts = pp + .window(f1, v -> Flowable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + pp.onNext(1); + pp.onError(new TestException()); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + + ts.assertError(TestException.class); + } + + @Test + public void windowOpenIgnoresDispose() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishProcessor pp = PublishProcessor.create(); + Flowable f1 = Flowable.unsafeCreate(ref1::set); + + TestSubscriber> ts = pp + .window(f1, v -> Flowable.never()) + .take(1) + .doOnNext(w -> { + w.test(); + }) + .test(); + + ref1.get().onSubscribe(new BooleanSubscription()); + ref1.get().onNext(1); + ref1.get().onNext(2); + + ts.assertValueCount(1); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().window(Flowable.never(), v -> Flowable.never())); + } + + @Test + public void mainIgnoresCancelBeforeOnError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Flowable.fromPublisher(s -> { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onError(new IOException()); + }) + .window(BehaviorProcessor.createDefault(1), v -> Flowable.error(new TestException())) + .doOnNext(w -> w.test()) + .test() + .assertError(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java index eb72dab5f7..6982b00cea 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWindowWithTimeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FlowableWindowWithTimeTest extends RxJavaTest { @@ -46,8 +46,8 @@ public void before() { @Test public void timedAndCount() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); Flowable source = Flowable.unsafeCreate(new Publisher() { @Override @@ -82,8 +82,8 @@ public void subscribe(Subscriber subscriber) { @Test public void timed() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); Flowable source = Flowable.unsafeCreate(new Publisher() { @Override @@ -111,7 +111,7 @@ public void subscribe(Subscriber subscriber) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -143,7 +143,7 @@ public void accept(Flowable stringFlowable) { stringFlowable.subscribe(new DefaultSubscriber() { @Override public void onComplete() { - lists.add(new ArrayList(list)); + lists.add(new ArrayList<>(list)); list.clear(); } @@ -166,8 +166,8 @@ public void exactWindowSize() { Flowable> source = Flowable.range(1, 10) .window(1, TimeUnit.MINUTES, scheduler, 3); - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); source.subscribe(observeWindow(list, lists)); @@ -184,7 +184,7 @@ public void exactWindowSize() { @Test public void takeFlatMapCompletes() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicInteger wip = new AtomicInteger(); @@ -434,7 +434,6 @@ public void skipOnError() { } @Test - @SuppressWarnings("unchecked") public void exactBackpressure() { TestScheduler scheduler = new TestScheduler(); @@ -449,7 +448,6 @@ public void exactBackpressure() { } @Test - @SuppressWarnings("unchecked") public void skipBackpressure() { TestScheduler scheduler = new TestScheduler(); @@ -464,7 +462,6 @@ public void skipBackpressure() { } @Test - @SuppressWarnings("unchecked") public void overlapBackpressure() { TestScheduler scheduler = new TestScheduler(); @@ -514,7 +511,7 @@ public void overlapBackpressure2() { PublishProcessor pp = PublishProcessor.create(); - final TestSubscriber tsInner = new TestSubscriber(); + final TestSubscriber tsInner = new TestSubscriber<>(); TestSubscriber> ts = pp.window(2, 1, TimeUnit.SECONDS, scheduler) .doOnNext(new Consumer>() { @@ -559,6 +556,7 @@ public void restartTimer() { } @Test + @SuppressUndeliverable public void exactBoundaryError() { Flowable.error(new TestException()) .window(1, TimeUnit.DAYS, Schedulers.single(), 2, true) @@ -866,7 +864,6 @@ public Publisher> apply(Flowable f) }); } - @SuppressWarnings("unchecked") @Test public void firstWindowMissingBackpressure() { Flowable.never() @@ -977,6 +974,7 @@ public void accept(Flowable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -1057,6 +1055,7 @@ public void accept(Flowable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -1137,6 +1136,7 @@ public void accept(Flowable v) throws Exception { } @Test + @SuppressUndeliverable public void skipTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -1204,7 +1204,7 @@ public Publisher apply(Flowable w) throws Throwable { public void windowAbandonmentCancelsUpstreamExactTime() { PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); TestSubscriber> ts = pp.window(10, TimeUnit.MINUTES) .take(1) @@ -1254,7 +1254,7 @@ public Publisher apply(Flowable w) throws Throwable { public void windowAbandonmentCancelsUpstreamExactTimeAndSize() { PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); TestSubscriber> ts = pp.window(10, TimeUnit.MINUTES, 100) .take(1) @@ -1304,7 +1304,7 @@ public Publisher apply(Flowable w) throws Throwable { public void windowAbandonmentCancelsUpstreamExactTimeSkip() { PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); TestSubscriber> ts = pp.window(10, 15, TimeUnit.MINUTES) .take(1) @@ -1325,5 +1325,27 @@ public void accept(Flowable v) throws Throwable { inner.get().test().assertResult(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Flowable.never().window(1, TimeUnit.SECONDS)); + } + + @Test + public void timedBoundarySignalAndDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestScheduler scheduler = new TestScheduler(); + + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber> ts = pp.window(1, TimeUnit.MINUTES, scheduler, 1) + .test(); + + TestHelper.race( + () -> pp.onNext(1), + () -> ts.cancel() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromTest.java index 20814684da..df325cd2c8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableWithLatestFromTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -90,7 +90,7 @@ public void emptySource() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -116,7 +116,7 @@ public void emptyOther() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -142,7 +142,7 @@ public void unsubscription() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); result.subscribe(ts); @@ -169,7 +169,7 @@ public void sourceThrows() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -197,7 +197,7 @@ public void otherThrows() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -225,7 +225,7 @@ public void functionThrows() { Flowable result = source.withLatestFrom(other, COMBINER_ERROR); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -251,7 +251,7 @@ public void noDownstreamUnsubscribe() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); result.subscribe(ts); @@ -267,7 +267,7 @@ public void backpressure() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); result.subscribe(ts); @@ -319,7 +319,7 @@ public void manySources() { PublishProcessor pp3 = PublishProcessor.create(); PublishProcessor main = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); main.withLatestFrom(new Flowable[] { pp1, pp2, pp3 }, toArray) .subscribe(ts); @@ -366,7 +366,7 @@ public void manySourcesIterable() { PublishProcessor pp3 = PublishProcessor.create(); PublishProcessor main = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); main.withLatestFrom(Arrays.>asList(pp1, pp2, pp3), toArray) .subscribe(ts); @@ -411,8 +411,8 @@ public void manySourcesIterableSweep() { for (String val : new String[] { "1" /*, null*/ }) { int n = 35; for (int i = 0; i < n; i++) { - List> sources = new ArrayList>(); - List expected = new ArrayList(); + List> sources = new ArrayList<>(); + List expected = new ArrayList<>(); expected.add(val); for (int j = 0; j < i; j++) { @@ -420,7 +420,7 @@ public void manySourcesIterableSweep() { expected.add(String.valueOf(val)); } - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); PublishProcessor main = PublishProcessor.create(); @@ -443,7 +443,7 @@ public void backpressureNoSignal() { PublishProcessor pp1 = PublishProcessor.create(); PublishProcessor pp2 = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.range(1, 10).withLatestFrom(new Flowable[] { pp1, pp2 }, toArray) .subscribe(ts); @@ -465,7 +465,7 @@ public void backpressureWithSignal() { PublishProcessor pp1 = PublishProcessor.create(); PublishProcessor pp2 = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.range(1, 3).withLatestFrom(new Flowable[] { pp1, pp2 }, toArray) .subscribe(ts); @@ -495,7 +495,7 @@ public void backpressureWithSignal() { @Test public void withEmpty() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.range(1, 3).withLatestFrom( new Flowable[] { Flowable.just(1), Flowable.empty() }, toArray) @@ -508,7 +508,7 @@ public void withEmpty() { @Test public void withError() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.range(1, 3).withLatestFrom( new Flowable[] { Flowable.just(1), Flowable.error(new TestException()) }, toArray) @@ -521,7 +521,7 @@ public void withError() { @Test public void withMainError() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.error(new TestException()).withLatestFrom( new Flowable[] { Flowable.just(1), Flowable.just(1) }, toArray) @@ -536,7 +536,7 @@ public void withMainError() { public void with2Others() { Flowable just = Flowable.just(1); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); just.withLatestFrom(just, just, new Function3>() { @Override @@ -555,7 +555,7 @@ public List apply(Integer a, Integer b, Integer c) { public void with3Others() { Flowable just = Flowable.just(1); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); just.withLatestFrom(just, just, just, new Function4>() { @Override @@ -574,7 +574,7 @@ public List apply(Integer a, Integer b, Integer c, Integer d) { public void with4Others() { Flowable just = Flowable.just(1); - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); just.withLatestFrom(just, just, just, just, new Function5>() { @Override @@ -609,7 +609,7 @@ public Object apply(Integer a, Integer b, Integer c) throws Exception { @Test public void manyIteratorThrows() { Flowable.just(1) - .withLatestFrom(new CrashingMappedIterable>(1, 100, 100, new Function>() { + .withLatestFrom(new CrashingMappedIterable<>(1, 100, 100, new Function>() { @Override public Flowable apply(Integer v) throws Exception { return Flowable.just(2); @@ -704,7 +704,6 @@ public Object apply(Integer a, Integer b) throws Exception { .assertFailure(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void combineToNull2() { Flowable.just(1) @@ -733,7 +732,7 @@ public void singleRequestNotForgottenWhenNoData() { Flowable result = source.withLatestFrom(other, COMBINER); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); result.subscribe(ts); @@ -794,7 +793,7 @@ public Object apply(Object a, Integer b, Integer c, Integer d) } }); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -837,7 +836,7 @@ public Object apply(Object a, Integer b, Integer c, Integer d) } }); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipCompletionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipCompletionTest.java index 3de4aa1e39..fe69ea6be5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipCompletionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipCompletionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterableTest.java index 871b603383..aee49596d9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java index d6921bfd91..8d3e10dbd2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/FlowableZipTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,8 +30,8 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueSubscription; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -352,7 +352,7 @@ public void aggregatorUnsubscribe() { PublishProcessor r2 = PublishProcessor.create(); /* define a Subscriber to receive aggregated events */ Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); Flowable.zip(r1, r2, zipr2).subscribe(ts); @@ -770,7 +770,7 @@ public String apply(Integer a, Integer b) { } }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); os.subscribe(new Consumer() { @Override @@ -797,7 +797,7 @@ public String apply(Integer a, Integer b) { } }).take(5); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); os.subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); @@ -822,7 +822,7 @@ public String apply(Integer a, Integer b) { } }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); os.subscribe(new DefaultSubscriber() { @Override @@ -886,7 +886,7 @@ public String apply(Notification t1, Notification t2) { }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); f.subscribe(new Consumer() { @Override @@ -915,7 +915,7 @@ public String apply(Integer t1, String t2) { }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); f.subscribe(new Consumer() { @Override @@ -942,7 +942,7 @@ public Object apply(final Object[] args) { } }); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); f.subscribe(ts); ts.awaitDone(200, TimeUnit.MILLISECONDS); ts.assertNoValues(); @@ -976,7 +976,7 @@ public void backpressureSync() { Flowable f1 = createInfiniteFlowable(generatedA); Flowable f2 = createInfiniteFlowable(generatedB); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.zip(f1, f2, new BiFunction() { @Override @@ -1000,7 +1000,7 @@ public void backpressureAsync() { Flowable f1 = createInfiniteFlowable(generatedA).subscribeOn(Schedulers.computation()); Flowable f2 = createInfiniteFlowable(generatedB).subscribeOn(Schedulers.computation()); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.zip(f1, f2, new BiFunction() { @Override @@ -1024,7 +1024,7 @@ public void downstreamBackpressureRequestsWithFiniteSyncFlowables() { Flowable f1 = createInfiniteFlowable(generatedA).take(Flowable.bufferSize() * 2); Flowable f2 = createInfiniteFlowable(generatedB).take(Flowable.bufferSize() * 2); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.zip(f1, f2, new BiFunction() { @Override @@ -1049,7 +1049,7 @@ public void downstreamBackpressureRequestsWithInfiniteAsyncFlowables() { Flowable f1 = createInfiniteFlowable(generatedA).subscribeOn(Schedulers.computation()); Flowable f2 = createInfiniteFlowable(generatedB).subscribeOn(Schedulers.computation()); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.zip(f1, f2, new BiFunction() { @Override @@ -1074,7 +1074,7 @@ public void downstreamBackpressureRequestsWithInfiniteSyncFlowables() { Flowable f1 = createInfiniteFlowable(generatedA); Flowable f2 = createInfiniteFlowable(generatedB); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.zip(f1, f2, new BiFunction() { @Override @@ -1189,7 +1189,7 @@ public Integer apply(Integer i1, Integer i2) { return i1 + i2; } }); - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 1026; i++) { expected.add(i * 3); } @@ -1247,7 +1247,7 @@ public Integer apply(Integer t1, Integer t2) { @Test public void zipRequest1() { Flowable src = Flowable.just(1).subscribeOn(Schedulers.computation()); - TestSubscriber ts = new TestSubscriber(1L); + TestSubscriber ts = new TestSubscriber<>(1L); Flowable.zip(src, src, new BiFunction() { @Override @@ -1297,7 +1297,7 @@ public void zipNArguments() throws Exception { if (j < i) { assertEquals("source" + (j + 1) + " is null", ex.getCause().getMessage()); } else { - assertEquals("f is null", ex.getCause().getMessage()); + assertEquals("zipper is null", ex.getCause().getMessage()); } } } @@ -1422,7 +1422,6 @@ public Object apply(Integer a, Integer b) throws Exception { .assertResult("929"); } - @SuppressWarnings("unchecked") @Test public void zipArrayEmpty() { assertSame(Flowable.empty(), Flowable.zipArray(Functions.identity(), false, 16)); @@ -1871,7 +1870,7 @@ public Integer apply(Integer a, Integer b) throws Exception { public void firstErrorPreventsSecondSubscription() { final AtomicInteger counter = new AtomicInteger(); - List> flowableList = new ArrayList>(); + List> flowableList = new ArrayList<>(); flowableList.add(Flowable.create(new FlowableOnSubscribe() { @Override public void subscribe(FlowableEmitter e) @@ -1897,7 +1896,6 @@ public Object apply(Object[] a) throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void publishersInIterable() { Publisher source = new Publisher() { @@ -1916,4 +1914,30 @@ public Integer apply(Object[] t) throws Throwable { .test() .assertResult(2); } + + @Test + public void fusedInnerPollCrashDelayError() { + Flowable.zip( + Flowable.range(1, 5), + Flowable.just(1) + .map(v -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()), + (a, b) -> a + b, true + ) + .test() + .assertFailure(TestException.class); + } + + @Test + public void fusedInnerPollCrashRequestBoundaryDelayError() { + Flowable.zip( + Flowable.range(1, 5), + Flowable.just(1) + .map(v -> { throw new TestException(); }) + .compose(TestHelper.flowableStripBoundary()), + (a, b) -> a + b, true + ) + .test(0L) + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/NotificationLiteTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/NotificationLiteTest.java index 1df91fbe4d..2c4b42aa9c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/NotificationLiteTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/flowable/NotificationLiteTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,10 +15,10 @@ import static org.junit.Assert.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.NotificationLite; @@ -80,7 +80,7 @@ public void completeNotification() { @Test public void disposableNotification() { - Object o = NotificationLite.disposable(Disposables.empty()); + Object o = NotificationLite.disposable(Disposable.empty()); assertEquals("NotificationLite.Disposable[RunnableDisposable(disposed=false, EmptyRunnable)]", o.toString()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstreamTest.java index 0e953b262f..dca854d3c1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstreamTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/AbstractMaybeWithUpstreamTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmbTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmbTest.java index 36addaab95..5475010163 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmbTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeAmbTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,10 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -37,7 +37,7 @@ public class MaybeAmbTest extends RxJavaTest { @Test public void ambLots() { - List> ms = new ArrayList>(); + List> ms = new ArrayList<>(); for (int i = 0; i < 32; i++) { ms.add(Maybe.never()); @@ -50,7 +50,6 @@ public void ambLots() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void ambFirstDone() { Maybe.amb(Arrays.asList(Maybe.just(1), Maybe.just(2))) @@ -58,7 +57,6 @@ public void ambFirstDone() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void dispose() { PublishProcessor pp1 = PublishProcessor.create(); @@ -76,7 +74,6 @@ public void dispose() { assertFalse(pp2.hasSubscribers()); } - @SuppressWarnings("unchecked") @Test public void innerErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -119,12 +116,11 @@ public void run() { @Test public void disposeNoFurtherSignals() { - @SuppressWarnings("unchecked") TestObserver to = Maybe.ambArray(new Maybe() { @Override protected void subscribeActual( MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(1); observer.onSuccess(2); observer.onComplete(); @@ -137,7 +133,6 @@ protected void subscribeActual( to.assertResult(1); } - @SuppressWarnings("unchecked") @Test public void noWinnerSuccessDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -163,7 +158,6 @@ public void accept(Object v) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerErrorDispose() throws Exception { final TestException ex = new TestException(); @@ -190,7 +184,6 @@ public void accept(Throwable e) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerCompleteDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -226,7 +219,6 @@ public void nullSourceSuccessRace() { final Subject ps = ReplaySubject.create(); ps.onNext(1); - @SuppressWarnings("unchecked") final Maybe source = Maybe.ambArray(ps.singleElement(), Maybe.never(), Maybe.never(), null); @@ -255,7 +247,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void maybeSourcesInIterable() { MaybeSource source = new MaybeSource() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeBlockingSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeBlockingSubscribeTest.java new file mode 100644 index 0000000000..e11548cdc9 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeBlockingSubscribeTest.java @@ -0,0 +1,502 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeBlockingSubscribeTest { + + @Test + public void noArgSuccess() { + Maybe.just(1) + .blockingSubscribe(); + } + + @Test + public void noArgSuccessAsync() { + Maybe.just(1) + .delay(100, TimeUnit.MILLISECONDS) + .blockingSubscribe(); + } + + @Test + public void noArgEmpty() { + Maybe.empty() + .blockingSubscribe(); + } + + @Test + public void noArgEmptyAsync() { + Maybe.empty() + .delay(100, TimeUnit.MILLISECONDS) + .blockingSubscribe(); + } + + @Test + public void noArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Maybe.error(new TestException()) + .blockingSubscribe(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void noArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Maybe.error(new TestException()) + .delay(100, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void oneArgSuccess() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Maybe.just(1) + .blockingSubscribe(success); + + verify(success).accept(1); + } + + @Test + public void oneArgSuccessAsync() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Maybe.just(1) + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(success); + + verify(success).accept(1); + } + + @Test + public void oneArgEmpty() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Maybe.empty() + .blockingSubscribe(success); + + verify(success, never()).accept(any()); + } + + @Test + public void oneArgEmptyAsync() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Maybe.empty() + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(success); + + verify(success, never()).accept(any()); + } + + @Test + public void oneArgSuccessFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + doThrow(new TestException()).when(success).accept(any()); + + Maybe.just(1) + .blockingSubscribe(success); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success).accept(1); + }); + } + + @Test + public void oneArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Maybe.error(new TestException()) + .blockingSubscribe(success); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + }); + } + + @Test + public void oneArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Maybe.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(success); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + }); + } + + @Test + public void twoArgSuccess() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.just(1) + .blockingSubscribe(success, consumer); + + verify(success).accept(1); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgSuccessAsync() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.just(1) + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(success, consumer); + + verify(success).accept(any()); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgEmpty() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.empty() + .blockingSubscribe(success, consumer); + + verify(success, never()).accept(any()); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgEmptyAsync() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.empty() + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(success, consumer); + + verify(success, never()).accept(any()); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgSuccessFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + doThrow(new TestException()).when(success).accept(any()); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.just(1) + .blockingSubscribe(success, consumer); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success).accept(any()); + verify(consumer, never()).accept(any()); + }); + } + + @Test + public void twoArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.error(new TestException()) + .blockingSubscribe(success, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Maybe.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(success, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgErrorFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + doThrow(new TestException()).when(consumer).accept(any()); + + Maybe.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(success, consumer); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void threeArgSuccess() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + Action action = mock(Action.class); + + Maybe.just(1) + .blockingSubscribe(success, consumer, action); + + verify(success).accept(any()); + verify(consumer, never()).accept(any(Throwable.class)); + verify(action, never()).run(); + } + + @Test + public void threeArgEmpty() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + Action action = mock(Action.class); + + Maybe.empty() + .blockingSubscribe(success, consumer, action); + + verify(success, never()).accept(any()); + verify(consumer, never()).accept(any(Throwable.class)); + verify(action).run(); + } + + @Test + public void threeArgError() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + Action action = mock(Action.class); + + Maybe.error(new TestException()) + .blockingSubscribe(success, consumer, action); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + verify(action, never()).run(); + } + + @Test + public void threeArgEmptyFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Action action = mock(Action.class); + doThrow(new TestException()).when(action).run(); + + Maybe.empty() + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(success, consumer, action); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + verify(consumer, never()).accept(any()); + verify(action).run(); + }); + } + + @Test + public void threeArgInterrupted() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action onDispose = mock(Action.class); + + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + Action action = mock(Action.class); + + Thread.currentThread().interrupt(); + + Maybe.never() + .doOnDispose(onDispose) + .blockingSubscribe(success, consumer, action); + + assertTrue("" + errors, errors.isEmpty()); + + verify(onDispose).run(); + verify(success, never()).accept(any()); + verify(action, never()).run(); + verify(consumer).accept(any(InterruptedException.class)); + }); + } + + @Test + public void observerSuccess() { + TestObserver to = new TestObserver<>(); + + Maybe.just(1) + .blockingSubscribe(to); + + to.assertResult(1); + } + + @Test + public void observerSuccessAsync() { + TestObserver to = new TestObserver<>(); + + Maybe.just(1) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(to); + + to.assertResult(1); + } + + @Test + public void observerEmpty() { + TestObserver to = new TestObserver<>(); + + Maybe.empty() + .blockingSubscribe(to); + + to.assertResult(); + } + + @Test + public void observerEmptyAsync() { + TestObserver to = new TestObserver<>(); + + Maybe.empty() + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(to); + + to.assertResult(); + } + + @Test + public void observerError() { + TestObserver to = new TestObserver<>(); + + Maybe.error(new TestException()) + .blockingSubscribe(to); + + to.assertFailure(TestException.class); + } + + @Test + public void observerErrorAsync() { + TestObserver to = new TestObserver<>(); + + Maybe.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation()) + .blockingSubscribe(to); + + to.assertFailure(TestException.class); + } + + @Test + public void observerDispose() throws Throwable { + Action onDispose = mock(Action.class); + + TestObserver to = new TestObserver<>(); + to.dispose(); + + Maybe.never() + .doOnDispose(onDispose) + .blockingSubscribe(to); + + to.assertEmpty(); + + verify(onDispose).run(); + } + + @Test + public void ovserverInterrupted() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action onDispose = mock(Action.class); + + TestObserver to = new TestObserver<>(); + + Thread.currentThread().interrupt(); + + Maybe.never() + .doOnDispose(onDispose) + .blockingSubscribe(to); + + assertTrue("" + errors, errors.isEmpty()); + + verify(onDispose).run(); + to.assertFailure(InterruptedException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCacheTest.java index 41c6d485eb..80f61f5931 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCacheTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -154,7 +154,7 @@ public void onlineComplete() { @Test public void crossCancelOnSuccess() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); PublishProcessor pp = PublishProcessor.create(); @@ -178,7 +178,7 @@ public void accept(Integer v) throws Exception { @Test public void crossCancelOnError() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); PublishProcessor pp = PublishProcessor.create(); @@ -201,7 +201,7 @@ public void accept(Object v) throws Exception { @Test public void crossCancelOnComplete() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); PublishProcessor pp = PublishProcessor.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserverTest.java index 4419d5e8c3..1758a1116b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCallbackObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,9 +31,9 @@ public class MaybeCallbackObserverTest extends RxJavaTest { @Test public void dispose() { - MaybeCallbackObserver mo = new MaybeCallbackObserver(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION); + MaybeCallbackObserver mo = new MaybeCallbackObserver<>(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); mo.onSubscribe(d); @@ -50,7 +50,7 @@ public void dispose() { public void onSuccessCrashes() { List errors = TestHelper.trackPluginErrors(); try { - MaybeCallbackObserver mo = new MaybeCallbackObserver( + MaybeCallbackObserver mo = new MaybeCallbackObserver<>( new Consumer() { @Override public void accept(Object v) throws Exception { @@ -60,7 +60,7 @@ public void accept(Object v) throws Exception { Functions.emptyConsumer(), Functions.EMPTY_ACTION); - mo.onSubscribe(Disposables.empty()); + mo.onSubscribe(Disposable.empty()); mo.onSuccess(1); @@ -74,7 +74,7 @@ public void accept(Object v) throws Exception { public void onErrorCrashes() { List errors = TestHelper.trackPluginErrors(); try { - MaybeCallbackObserver mo = new MaybeCallbackObserver( + MaybeCallbackObserver mo = new MaybeCallbackObserver<>( Functions.emptyConsumer(), new Consumer() { @Override @@ -84,7 +84,7 @@ public void accept(Object v) throws Exception { }, Functions.EMPTY_ACTION); - mo.onSubscribe(Disposables.empty()); + mo.onSubscribe(Disposable.empty()); mo.onError(new TestException("Outer")); @@ -103,7 +103,7 @@ public void accept(Object v) throws Exception { public void onCompleteCrashes() { List errors = TestHelper.trackPluginErrors(); try { - MaybeCallbackObserver mo = new MaybeCallbackObserver( + MaybeCallbackObserver mo = new MaybeCallbackObserver<>( Functions.emptyConsumer(), Functions.emptyConsumer(), new Action() { @@ -113,7 +113,7 @@ public void run() throws Exception { } }); - mo.onSubscribe(Disposables.empty()); + mo.onSubscribe(Disposable.empty()); mo.onComplete(); @@ -125,7 +125,7 @@ public void run() throws Exception { @Test public void onErrorMissingShouldReportNoCustomOnError() { - MaybeCallbackObserver o = new MaybeCallbackObserver(Functions.emptyConsumer(), + MaybeCallbackObserver o = new MaybeCallbackObserver<>(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION); @@ -134,7 +134,7 @@ public void onErrorMissingShouldReportNoCustomOnError() { @Test public void customOnErrorShouldReportCustomOnError() { - MaybeCallbackObserver o = new MaybeCallbackObserver(Functions.emptyConsumer(), + MaybeCallbackObserver o = new MaybeCallbackObserver<>(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayEagerDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayEagerDelayErrorTest.java new file mode 100644 index 0000000000..94e77bf55a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayEagerDelayErrorTest.java @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.exceptions.TestException; + +public class MaybeConcatArrayEagerDelayErrorTest { + + @Test + public void normal() { + Maybe.concatArrayEagerDelayError( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + ) + .test() + .assertFailure(TestException.class, 1, 2); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java index 68ccd8434b..c2d6fcc0ae 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatArrayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,15 +21,15 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeConcatArrayTest extends RxJavaTest { - @SuppressWarnings("unchecked") @Test public void cancel() { Maybe.concatArray(Maybe.just(1), Maybe.just(2)) @@ -38,7 +38,6 @@ public void cancel() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void cancelDelayError() { Maybe.concatArrayDelayError(Maybe.just(1), Maybe.just(2)) @@ -47,7 +46,6 @@ public void cancelDelayError() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void backpressure() { TestSubscriber ts = Maybe.concatArray(Maybe.just(1), Maybe.just(2)) @@ -64,7 +62,6 @@ public void backpressure() { ts.assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void backpressureDelayError() { TestSubscriber ts = Maybe.concatArrayDelayError(Maybe.just(1), Maybe.just(2)) @@ -81,7 +78,6 @@ public void backpressureDelayError() { ts.assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void requestCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -106,7 +102,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void requestCancelRaceDelayError() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -131,7 +126,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void errorAfterTermination() { List errors = TestHelper.trackPluginErrors(); @@ -142,7 +136,7 @@ public void errorAfterTermination() { new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(2); o[0] = observer; } @@ -158,7 +152,6 @@ protected void subscribeActual(MaybeObserver observer) { } } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscription() { final int[] calls = { 0 }; @@ -178,7 +171,6 @@ public void subscribe(MaybeEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionDelayError() { final int[] calls = { 0 }; @@ -197,4 +189,79 @@ public void subscribe(MaybeEmitter s) throws Exception { assertEquals(1, calls[0]); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Maybe.concatArray(MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void badRequestDelayError() { + TestHelper.assertBadRequestReported(Maybe.concatArrayDelayError(MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void mixed() { + Maybe.concatArray( + Maybe.just(1), + Maybe.empty(), + Maybe.just(2), + Maybe.empty(), + Maybe.empty() + ) + .test() + .assertResult(1, 2); + } + + @Test + public void requestBeforeSuccess() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArray(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onSuccess(1); + + ts.assertResult(1, 1); + } + + @Test + public void requestBeforeComplete() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArray(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onComplete(); + + ts.assertResult(); + } + + @Test + public void requestBeforeSuccessDelayError() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArrayDelayError(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onSuccess(1); + + ts.assertResult(1, 1); + } + + @Test + public void requestBeforeCompleteDelayError() { + MaybeSubject ms = MaybeSubject.create(); + TestSubscriber ts = Maybe.concatArrayDelayError(ms, ms) + .test(); + + ts.assertEmpty(); + + ms.onComplete(); + + ts.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatEagerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatEagerTest.java new file mode 100644 index 0000000000..4c53fcddf3 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatEagerTest.java @@ -0,0 +1,152 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import java.util.Arrays; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class MaybeConcatEagerTest { + + @Test + public void iterableNormal() { + Maybe.concatEager(Arrays.asList( + Maybe.just(1), + Maybe.empty(), + Maybe.just(2) + )) + .test() + .assertResult(1, 2); + } + + @Test + public void iterableNormalMaxConcurrency() { + Maybe.concatEager(Arrays.asList( + Maybe.just(1), + Maybe.empty(), + Maybe.just(2) + ), 1) + .test() + .assertResult(1, 2); + } + + @Test + public void iterableError() { + Maybe.concatEager(Arrays.asList( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + )) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void iterableErrorMaxConcurrency() { + Maybe.concatEager(Arrays.asList( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void publisherNormal() { + Maybe.concatEager(Flowable.fromArray( + Maybe.just(1), + Maybe.empty(), + Maybe.just(2) + )) + .test() + .assertResult(1, 2); + } + + @Test + public void publisherNormalMaxConcurrency() { + Maybe.concatEager(Flowable.fromArray( + Maybe.just(1), + Maybe.empty(), + Maybe.just(2) + ), 1) + .test() + .assertResult(1, 2); + } + + @Test + public void publisherError() { + Maybe.concatEager(Flowable.fromArray( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + )) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void iterableDelayError() { + Maybe.concatEagerDelayError(Arrays.asList( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + )) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void iterableDelayErrorMaxConcurrency() { + Maybe.concatEagerDelayError(Arrays.asList( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void publisherDelayError() { + Maybe.concatEagerDelayError(Flowable.fromArray( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + )) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void publisherDelayErrorMaxConcurrency() { + Maybe.concatEagerDelayError(Flowable.fromArray( + Maybe.just(1), + Maybe.error(new TestException()), + Maybe.empty(), + Maybe.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1, 2); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java index 4ceb82f1e6..c8b9045263 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,12 +24,12 @@ import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.util.CrashingMappedIterable; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeConcatIterableTest extends RxJavaTest { - @SuppressWarnings("unchecked") @Test public void take() { Maybe.concat(Arrays.asList(Maybe.just(1), Maybe.just(2), Maybe.just(3))) @@ -50,7 +50,6 @@ public Iterator> iterator() { .assertFailureAndMessage(TestException.class, "iterator()"); } - @SuppressWarnings("unchecked") @Test public void error() { Maybe.concat(Arrays.asList(Maybe.just(1), Maybe.error(new TestException()), Maybe.just(3))) @@ -58,7 +57,6 @@ public void error() { .assertFailure(TestException.class, 1); } - @SuppressWarnings("unchecked") @Test public void successCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -90,7 +88,7 @@ public void run() { @Test public void hasNextThrows() { - Maybe.concat(new CrashingMappedIterable>(100, 1, 100, new Function>() { + Maybe.concat(new CrashingMappedIterable<>(100, 1, 100, new Function>() { @Override public Maybe apply(Integer v) throws Exception { return Maybe.just(1); @@ -102,7 +100,7 @@ public Maybe apply(Integer v) throws Exception { @Test public void nextThrows() { - Maybe.concat(new CrashingMappedIterable>(100, 100, 1, new Function>() { + Maybe.concat(new CrashingMappedIterable<>(100, 100, 1, new Function>() { @Override public Maybe apply(Integer v) throws Exception { return Maybe.just(1); @@ -114,7 +112,7 @@ public Maybe apply(Integer v) throws Exception { @Test public void nextReturnsNull() { - Maybe.concat(new CrashingMappedIterable>(100, 100, 100, new Function>() { + Maybe.concat(new CrashingMappedIterable<>(100, 100, 100, new Function>() { @Override public Maybe apply(Integer v) throws Exception { return null; @@ -124,7 +122,6 @@ public Maybe apply(Integer v) throws Exception { .assertFailure(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscription() { final int[] calls = { 0 }; @@ -144,7 +141,6 @@ public void subscribe(MaybeEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionDelayError() { final int[] calls = { 0 }; @@ -163,4 +159,12 @@ public void subscribe(MaybeEmitter s) throws Exception { assertEquals(1, calls[0]); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(Maybe.concat(Arrays.asList( + MaybeSubject.create(), + MaybeSubject.create() + ))); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapCompletableTest.java new file mode 100644 index 0000000000..dedd03a02e --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapCompletableTest.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeConcatMapCompletableTest extends RxJavaTest { + + @Test + public void dispose() { + TestHelper.checkDisposed(Maybe.just(1).concatMapCompletable(new Function() { + @Override + public Completable apply(Integer v) throws Exception { + return Completable.complete(); + } + })); + } + + @Test + public void mapperThrows() { + Maybe.just(1) + .concatMapCompletable(new Function() { + @Override + public Completable apply(Integer v) throws Exception { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperReturnsNull() { + Maybe.just(1) + .concatMapCompletable(new Function() { + @Override + public Completable apply(Integer v) throws Exception { + return null; + } + }) + .test() + .assertFailure(NullPointerException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapSingleTest.java new file mode 100644 index 0000000000..93695b5293 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapSingleTest.java @@ -0,0 +1,142 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeConcatMapSingleTest extends RxJavaTest { + @Test + public void flatMapSingleElementValue() { + Maybe.just(1).concatMapSingle(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + if (integer == 1) { + return Single.just(2); + } + + return Single.just(1); + } + }) + .test() + .assertResult(2); + } + + @Test + public void flatMapSingleElementValueDifferentType() { + Maybe.just(1).concatMapSingle(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + if (integer == 1) { + return Single.just("2"); + } + + return Single.just("1"); + } + }) + .test() + .assertResult("2"); + } + + @Test + public void flatMapSingleElementValueNull() { + Maybe.just(1).concatMapSingle(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + return null; + } + }) + .to(TestHelper.testConsumer()) + .assertNoValues() + .assertError(NullPointerException.class) + .assertErrorMessage("The mapper returned a null SingleSource"); + } + + @Test + public void flatMapSingleElementValueErrorThrown() { + Maybe.just(1).concatMapSingle(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + throw new RuntimeException("something went terribly wrong!"); + } + }) + .to(TestHelper.testConsumer()) + .assertNoValues() + .assertError(RuntimeException.class) + .assertErrorMessage("something went terribly wrong!"); + } + + @Test + public void flatMapSingleElementError() { + RuntimeException exception = new RuntimeException("test"); + + Maybe.error(exception).concatMapSingle(new Function>() { + @Override public SingleSource apply(final Object integer) throws Exception { + return Single.just(new Object()); + } + }) + .test() + .assertError(exception); + } + + @Test + public void flatMapSingleElementEmpty() { + Maybe.empty().concatMapSingle(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + return Single.just(2); + } + }) + .test() + .assertNoValues() + .assertResult(); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Maybe.just(1).concatMapSingle(new Function>() { + @Override + public SingleSource apply(final Integer integer) throws Exception { + return Single.just(2); + } + })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(new Function, Maybe>() { + @Override + public Maybe apply(Maybe m) throws Exception { + return m.concatMapSingle(new Function>() { + @Override + public SingleSource apply(final Integer integer) throws Exception { + return Single.just(2); + } + }); + } + }); + } + + @Test + public void singleErrors() { + Maybe.just(1) + .concatMapSingle(new Function>() { + @Override + public SingleSource apply(final Integer integer) throws Exception { + return Single.error(new TestException()); + } + }) + .test() + .assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapTest.java new file mode 100644 index 0000000000..04e5abfafd --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatMapTest.java @@ -0,0 +1,101 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeConcatMapTest extends RxJavaTest { + + @Test + public void dispose() { + TestHelper.checkDisposed(Maybe.just(1).concatMap(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.just(2); + } + })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(new Function, MaybeSource>() { + @Override + public MaybeSource apply(Maybe v) throws Exception { + return v.concatMap(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.just(2); + } + }); + } + }); + } + + @Test + public void mainError() { + Maybe.error(new TestException()) + .concatMap(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.just(2); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mainEmpty() { + Maybe.empty() + .concatMap(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.just(2); + } + }) + .test() + .assertResult(); + } + + @Test + public void mapperThrows() { + Maybe.just(1) + .concatMap(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperReturnsNull() { + Maybe.just(1) + .concatMap(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return null; + } + }) + .test() + .assertFailure(NullPointerException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatPublisherTest.java index 527de9734b..ad334cac7b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeConcatPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.maybe; import java.util.concurrent.Callable; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContainsTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContainsTest.java index 07a7abc77b..ca019517d8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContainsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeContainsTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCountTest.java index 669cb4e28c..b223358ef2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCountTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreateTest.java index b9ffc12ee8..6bd42bce44 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeCreateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -79,7 +79,7 @@ public void onSuccessThrows() { Maybe.create(new MaybeOnSubscribe() { @Override public void subscribe(MaybeEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { @@ -121,7 +121,7 @@ public void onErrorThrows() { Maybe.create(new MaybeOnSubscribe() { @Override public void subscribe(MaybeEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { @@ -163,7 +163,7 @@ public void onCompleteThrows() { Maybe.create(new MaybeOnSubscribe() { @Override public void subscribe(MaybeEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherTest.java index 8ca50837ec..f482834e29 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayOtherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java index bca4023f83..dae5d6f68c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelaySubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -141,4 +141,9 @@ protected void subscribeActual(Subscriber subscriber) { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribePublisher() { + TestHelper.checkDoubleOnSubscribeFlowableToMaybe(f -> Maybe.just(1).delaySubscription(f)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayTest.java index da75df361c..4485f58f5e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDelayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -53,16 +53,6 @@ public void complete() { .assertResult(); } - @Test(expected = NullPointerException.class) - public void nullUnit() { - Maybe.just(1).delay(1, null); - } - - @Test(expected = NullPointerException.class) - public void nullScheduler() { - Maybe.just(1).delay(1, TimeUnit.MILLISECONDS, null); - } - @Test public void disposeDuringDelay() { TestScheduler scheduler = new TestScheduler(); @@ -106,4 +96,32 @@ public Maybe apply(Maybe f) throws Exception { } }); } + + @Test + public void delayedErrorOnSuccess() { + final TestScheduler scheduler = new TestScheduler(); + final TestObserver observer = Maybe.just(1) + .delay(5, TimeUnit.SECONDS, scheduler, true) + .test(); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + observer.assertNoValues(); + + scheduler.advanceTimeTo(5, TimeUnit.SECONDS); + observer.assertValue(1); + } + + @Test + public void delayedErrorOnError() { + final TestScheduler scheduler = new TestScheduler(); + final TestObserver observer = Maybe.error(new TestException()) + .delay(5, TimeUnit.SECONDS, scheduler, true) + .test(); + + scheduler.advanceTimeTo(2, TimeUnit.SECONDS); + observer.assertNoErrors(); + + scheduler.advanceTimeTo(5, TimeUnit.SECONDS); + observer.assertError(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDematerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDematerializeTest.java new file mode 100644 index 0000000000..4f78295f76 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDematerializeTest.java @@ -0,0 +1,122 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.mockito.Mockito.*; +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeDematerializeTest extends RxJavaTest { + + @Test + public void success() { + Maybe.just(Notification.createOnNext(1)) + .dematerialize(Functions.>identity()) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Maybe.just(Notification.createOnComplete()) + .dematerialize(Functions.>identity()) + .test() + .assertResult(); + } + + @Test + public void emptySource() throws Throwable { + @SuppressWarnings("unchecked") + Function, Notification> function = mock(Function.class); + + Maybe.>empty() + .dematerialize(function) + .test() + .assertResult(); + + verify(function, never()).apply(any()); + } + + @Test + public void error() { + Maybe.>error(new TestException()) + .dematerialize(Functions.>identity()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void errorNotification() { + Maybe.just(Notification.createOnError(new TestException())) + .dematerialize(Functions.>identity()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(new Function, MaybeSource>() { + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public MaybeSource apply(Maybe v) throws Exception { + return v.dematerialize((Function)Functions.identity()); + } + }); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(MaybeSubject.>create().dematerialize(Functions.>identity())); + } + + @Test + public void selectorCrash() { + Maybe.just(Notification.createOnNext(1)) + .dematerialize(new Function, Notification>() { + @Override + public Notification apply(Notification v) throws Exception { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void selectorNull() { + Maybe.just(Notification.createOnNext(1)) + .dematerialize(Functions.justFunction((Notification)null)) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void selectorDifferentType() { + Maybe.just(Notification.createOnNext(1)) + .dematerialize(new Function, Notification>() { + @Override + public Notification apply(Notification v) throws Exception { + return Notification.createOnNext("Value-" + 1); + } + }) + .test() + .assertResult("Value-1"); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetachTest.java index 25c2ce742a..6498e98371 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDetachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -64,8 +64,8 @@ public void onComplete() { @Test public void cancelDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Maybe() { @Override @@ -90,8 +90,8 @@ protected void subscribeActual(MaybeObserver observer) { @Test public void completeDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Maybe() { @Override @@ -116,8 +116,8 @@ protected void subscribeActual(MaybeObserver observer) { @Test public void errorDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Maybe() { @Override @@ -142,8 +142,8 @@ protected void subscribeActual(MaybeObserver observer) { @Test public void successDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Maybe() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccessTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccessTest.java index b754c5150f..a961f0b11c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccessTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoAfterSuccessTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class MaybeDoAfterSuccessTest extends RxJavaTest { - final List values = new ArrayList(); + final List values = new ArrayList<>(); final Consumer afterSuccess = new Consumer() { @Override @@ -77,11 +77,6 @@ public void empty() { assertTrue(values.isEmpty()); } - @Test(expected = NullPointerException.class) - public void consumerNull() { - Maybe.just(1).doAfterSuccess(null); - } - @Test public void justConditional() { Maybe.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinallyTest.java index 1be982960e..f98c3cf810 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoFinallyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -115,11 +115,6 @@ public void normalErrorConditional() { assertEquals(1, calls); } - @Test(expected = NullPointerException.class) - public void nullAction() { - Maybe.just(1).doFinally(null); - } - @Test public void actionThrows() { List errors = TestHelper.trackPluginErrors(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEventTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEventTest.java index 989599f2c6..37614ec793 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEventTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnEventTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -58,7 +58,7 @@ public void accept(Integer v, Throwable e) throws Exception { public void onSubscribeCrash() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable bs = Disposables.empty(); + final Disposable bs = Disposable.empty(); new Maybe() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnLifecycleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnLifecycleTest.java new file mode 100644 index 0000000000..39d71105ca --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnLifecycleTest.java @@ -0,0 +1,168 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeDoOnLifecycleTest extends RxJavaTest { + + @Test + public void success() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Maybe.just(1) + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertResult(1); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void empty() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Maybe.empty() + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertResult(); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Maybe.error(new TestException()) + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertFailure(TestException.class); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void onSubscribeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + doThrow(new TestException("First")).when(onSubscribe).accept(any()); + + Disposable bs = Disposable.empty(); + + new Maybe() { + @Override + protected void subscribeActual(MaybeObserver observer) { + observer.onSubscribe(bs); + observer.onError(new TestException("Second")); + observer.onComplete(); + observer.onSuccess(1); + } + } + .doOnLifecycle(onSubscribe, onDispose) + .to(TestHelper.testConsumer()) + .assertFailureAndMessage(TestException.class, "First"); + + assertTrue(bs.isDisposed()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "Second"); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + }); + } + + @Test + public void onDisposeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + doThrow(new TestException("First")).when(onDispose).run(); + + MaybeSubject ms = MaybeSubject.create(); + + TestObserver to = ms + .doOnLifecycle(onSubscribe, onDispose) + .test(); + + assertTrue(ms.hasObservers()); + + to.dispose(); + + assertFalse(ms.hasObservers()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "First"); + + verify(onSubscribe).accept(any()); + verify(onDispose).run(); + }); + } + + @Test + public void dispose() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + MaybeSubject ms = MaybeSubject.create(); + + TestObserver to = ms + .doOnLifecycle(onSubscribe, onDispose) + .test(); + + assertTrue(ms.hasObservers()); + + to.dispose(); + + assertFalse(ms.hasObservers()); + + verify(onSubscribe).accept(any()); + verify(onDispose).run(); + } + + @Test + public void isDisposed() { + TestHelper.checkDisposed(MaybeSubject.create().doOnLifecycle(d -> { }, () -> { })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(m -> m.doOnLifecycle(d -> { }, () -> { })); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminateTest.java index 8f93185207..4471db0ee1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeDoOnTerminateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,11 +27,6 @@ public class MaybeDoOnTerminateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void doOnTerminate() { - Maybe.just(1).doOnTerminate(null); - } - @Test public void doOnTerminateSuccess() { final AtomicBoolean atomicBoolean = new AtomicBoolean(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmptyTest.java index c477416241..a219718936 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.operators.ScalarSupplier; public class MaybeEmptyTest extends RxJavaTest { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualTest.java index 8a42f12d5b..e7b6ea4302 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeEqualTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorTest.java index b75eea1906..f67dc7884b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeErrorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingleTest.java index 2647f760ce..52682cc208 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFilterSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelectorTest.java index 513eb8eee4..67227bb422 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelectorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapBiSelectorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -215,7 +215,7 @@ public Object apply(Integer a, Integer b) throws Exception { @Test public void mapperCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Maybe.just(1) .flatMap(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletableTest.java index a76af597b2..053f1e3e40 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java index a702ac234d..e4bcaa9f77 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,10 +24,11 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -120,6 +121,20 @@ public Iterable apply(Integer v) throws Exception { .assertResult(1); } + @Test + public void take2() { + Maybe.just(1).flattenAsFlowable(new Function>() { + @Override + public Iterable apply(Integer v) throws Exception { + return Arrays.asList(v, v + 1); + } + }) + .doOnSubscribe(s -> s.request(Long.MAX_VALUE)) + .take(1) + .test() + .assertResult(1); + } + @Test public void fused() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); @@ -480,7 +495,7 @@ public void slowPathCancelAfterHasNext() { final Integer[] a = new Integer[1000]; Arrays.fill(a, 1); - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Maybe.just(1) .flattenAsFlowable(new Function>() { @@ -524,7 +539,7 @@ public void fastPathCancelAfterHasNext() { final Integer[] a = new Integer[1000]; Arrays.fill(a, 1); - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Maybe.just(1) .flattenAsFlowable(new Function>() { @@ -562,4 +577,33 @@ public void remove() { ts.request(Long.MAX_VALUE); ts.assertValues(1, 1).assertNoErrors().assertNotComplete(); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(MaybeSubject.create().flattenAsFlowable(v -> Arrays.asList(v))); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybeToFlowable(m -> m.flattenAsFlowable(v -> Arrays.asList(v))); + } + + @Test + public void onSuccessRequestRace() { + List list = Arrays.asList(1); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + MaybeSubject ms = MaybeSubject.create(); + + TestSubscriber ts = ms.flattenAsFlowable(v -> list) + .test(0L); + + TestHelper.race( + () -> ms.onSuccess(1), + () -> ts.request(1) + ); + + ts.assertResult(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservableTest.java index e9d955386a..d06f626119 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapIterableObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,8 +25,9 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -99,7 +100,7 @@ public Iterable apply(Integer v) throws Exception { @Test public void fused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Maybe.just(1).flattenAsObservable(new Function>() { @Override @@ -117,7 +118,7 @@ public Iterable apply(Integer v) throws Exception { @Test public void fusedNoSync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Maybe.just(1).flattenAsObservable(new Function>() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotificationTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotificationTest.java index 6faec2ab9a..1d4d4f0d5e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotificationTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapNotificationTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElementTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElementTest.java index c3409aed66..2f5cf42426 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElementTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleElementTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,15 +21,9 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFlatMapSingleElementTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void flatMapSingleElementNull() { - Maybe.just(1) - .flatMapSingleElement(null); - } - @Test - public void flatMapSingleElementValue() { - Maybe.just(1).flatMapSingleElement(new Function>() { + public void flatMapSingleValue() { + Maybe.just(1).flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { if (integer == 1) { return Single.just(2); @@ -43,8 +37,8 @@ public void flatMapSingleElementValue() { } @Test - public void flatMapSingleElementValueDifferentType() { - Maybe.just(1).flatMapSingleElement(new Function>() { + public void flatMapSingleValueDifferentType() { + Maybe.just(1).flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { if (integer == 1) { return Single.just("2"); @@ -58,8 +52,8 @@ public void flatMapSingleElementValueDifferentType() { } @Test - public void flatMapSingleElementValueNull() { - Maybe.just(1).flatMapSingleElement(new Function>() { + public void flatMapSingleValueNull() { + Maybe.just(1).flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { return null; } @@ -71,8 +65,8 @@ public void flatMapSingleElementValueNull() { } @Test - public void flatMapSingleElementValueErrorThrown() { - Maybe.just(1).flatMapSingleElement(new Function>() { + public void flatMapSingleValueErrorThrown() { + Maybe.just(1).flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { throw new RuntimeException("something went terribly wrong!"); } @@ -84,10 +78,10 @@ public void flatMapSingleElementValueErrorThrown() { } @Test - public void flatMapSingleElementError() { + public void flatMapSingleError() { RuntimeException exception = new RuntimeException("test"); - Maybe.error(exception).flatMapSingleElement(new Function>() { + Maybe.error(exception).flatMapSingle(new Function>() { @Override public SingleSource apply(final Object integer) throws Exception { return Single.just(new Object()); } @@ -97,8 +91,8 @@ public void flatMapSingleElementError() { } @Test - public void flatMapSingleElementEmpty() { - Maybe.empty().flatMapSingleElement(new Function>() { + public void flatMapSingleEmpty() { + Maybe.empty().flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { return Single.just(2); } @@ -110,7 +104,7 @@ public void flatMapSingleElementEmpty() { @Test public void dispose() { - TestHelper.checkDisposed(Maybe.just(1).flatMapSingleElement(new Function>() { + TestHelper.checkDisposed(Maybe.just(1).flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { return Single.just(2); @@ -123,7 +117,7 @@ public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeMaybe(new Function, Maybe>() { @Override public Maybe apply(Maybe m) throws Exception { - return m.flatMapSingleElement(new Function>() { + return m.flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { return Single.just(2); @@ -136,7 +130,7 @@ public SingleSource apply(final Integer integer) throws Exception { @Test public void singleErrors() { Maybe.just(1) - .flatMapSingleElement(new Function>() { + .flatMapSingle(new Function>() { @Override public SingleSource apply(final Integer integer) throws Exception { return Single.error(new TestException()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleTest.java index 60b1a98f3b..cdf7f36f1f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlatMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,12 +23,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFlatMapSingleTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void flatMapSingleNull() { - Maybe.just(1) - .flatMapSingle(null); - } - @Test public void flatMapSingleValue() { Maybe.just(1).flatMapSingle(new Function>() { @@ -40,6 +34,7 @@ public void flatMapSingleValue() { return Single.just(1); } }) + .toSingle() .test() .assertResult(2); } @@ -55,6 +50,7 @@ public void flatMapSingleValueDifferentType() { return Single.just("1"); } }) + .toSingle() .test() .assertResult("2"); } @@ -66,6 +62,7 @@ public void flatMapSingleValueNull() { return null; } }) + .toSingle() .to(TestHelper.testConsumer()) .assertNoValues() .assertError(NullPointerException.class) @@ -79,6 +76,7 @@ public void flatMapSingleValueErrorThrown() { throw new RuntimeException("something went terribly wrong!"); } }) + .toSingle() .to(TestHelper.testConsumer()) .assertNoValues() .assertError(RuntimeException.class) @@ -94,6 +92,7 @@ public void flatMapSingleError() { return Single.just(new Object()); } }) + .toSingle() .test() .assertError(exception); } @@ -105,6 +104,7 @@ public void flatMapSingleEmpty() { return Single.just(2); } }) + .toSingle() .test() .assertNoValues() .assertError(NoSuchElementException.class); @@ -117,7 +117,7 @@ public void dispose() { public SingleSource apply(final Integer integer) throws Exception { return Single.just(2); } - })); + }).toSingle()); } @Test @@ -130,7 +130,7 @@ public SingleSource apply(Maybe m) throws Exception { public SingleSource apply(final Integer integer) throws Exception { return Single.just(2); } - }); + }).toSingle(); } }); } @@ -144,6 +144,7 @@ public SingleSource apply(final Integer integer) throws Exception { return Single.error(new TestException()); } }) + .toSingle() .test() .assertFailure(TestException.class); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlattenTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlattenTest.java index bcdaa3f061..30c0cfdeb8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlattenTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFlattenTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromActionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromActionTest.java index c797e81d8d..32f37c1cd7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromActionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromActionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,11 +30,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFromActionTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromActionNull() { - Maybe.fromAction(null); - } - @Test public void fromAction() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -170,7 +165,7 @@ public void disposedUpfront() throws Throwable { @Test public void cancelWhileRunning() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Maybe.fromAction(new Action() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java index eb551defcd..dec0d8e33e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCallableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,11 +34,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFromCallableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromCallableNull() { - Maybe.fromCallable(null); - } - @Test public void fromCallable() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -196,7 +191,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromCallableObservable .subscribeOn(Schedulers.computation()) @@ -218,4 +213,18 @@ public String answer(InvocationOnMock invocation) throws Throwable { verify(observer).onSubscribe(any(Disposable.class)); verifyNoMoreInteractions(observer); } + + @Test + public void disposeUpfront() { + Maybe.fromCallable(() -> 1) + .test(true) + .assertEmpty(); + } + + @Test + public void success() { + Maybe.fromCallable(() -> 1) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletableTest.java index 3549901863..40f00e371a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,11 +24,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFromCompletableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromCompletableNull() { - Maybe.fromCompletable(null); - } - @Test public void fromCompletable() { Maybe.fromCompletable(Completable.complete()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFutureTest.java index 82ed2f9b35..fb88c60a72 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFutureTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromFutureTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public class MaybeFromFutureTest extends RxJavaTest { @Test public void cancelImmediately() { - FutureTask ft = new FutureTask(Functions.justCallable(1)); + FutureTask ft = new FutureTask<>(Functions.justCallable(1)); Maybe.fromFuture(ft).test(true) .assertEmpty(); @@ -37,7 +37,7 @@ public void cancelImmediately() { @Test public void timeout() { - FutureTask ft = new FutureTask(Functions.justCallable(1)); + FutureTask ft = new FutureTask<>(Functions.justCallable(1)); Maybe.fromFuture(ft, 1, TimeUnit.MILLISECONDS).test() .awaitDone(5, TimeUnit.SECONDS) @@ -46,7 +46,7 @@ public void timeout() { @Test public void timedWait() { - FutureTask ft = new FutureTask(Functions.justCallable(1)); + FutureTask ft = new FutureTask<>(Functions.justCallable(1)); ft.run(); Maybe.fromFuture(ft, 1, TimeUnit.MILLISECONDS).test() @@ -56,7 +56,7 @@ public void timedWait() { @Test public void interrupt() { - FutureTask ft = new FutureTask(Functions.justCallable(1)); + FutureTask ft = new FutureTask<>(Functions.justCallable(1)); Thread.currentThread().interrupt(); @@ -66,9 +66,9 @@ public void interrupt() { @Test public void cancelWhileRunning() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); - FutureTask ft = new FutureTask(new Runnable() { + FutureTask ft = new FutureTask<>(new Runnable() { @Override public void run() { to.dispose(); @@ -86,9 +86,9 @@ public void run() { @Test public void cancelAndCrashWhileRunning() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); - FutureTask ft = new FutureTask(new Runnable() { + FutureTask ft = new FutureTask<>(new Runnable() { @Override public void run() { to.dispose(); @@ -107,7 +107,7 @@ public void run() { @Test public void futureNull() { - FutureTask ft = new FutureTask(new Runnable() { + FutureTask ft = new FutureTask<>(new Runnable() { @Override public void run() { } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromObservableTest.java new file mode 100644 index 0000000000..311f33e4d4 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromObservableTest.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class MaybeFromObservableTest extends RxJavaTest { + + @Test + public void empty() { + Maybe.fromObservable(Observable.empty().hide()) + .test() + .assertResult(); + } + + @Test + public void just() { + Maybe.fromObservable(Observable.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void range() { + Maybe.fromObservable(Observable.range(1, 5).hide()) + .test() + .assertResult(1); + } + + @Test + public void error() { + Maybe.fromObservable(Observable.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromPubisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromPubisherTest.java new file mode 100644 index 0000000000..c51c23bae4 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromPubisherTest.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class MaybeFromPubisherTest extends RxJavaTest { + + @Test + public void empty() { + Maybe.fromPublisher(Flowable.empty().hide()) + .test() + .assertResult(); + } + + @Test + public void just() { + Maybe.fromPublisher(Flowable.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void range() { + Maybe.fromPublisher(Flowable.range(1, 5).hide()) + .test() + .assertResult(1); + } + + @Test + public void error() { + Maybe.fromPublisher(Flowable.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnableTest.java index 6c59ecca27..5d1db4ad4d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromRunnableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,11 +30,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFromRunnableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromRunnableNull() { - Maybe.fromRunnable(null); - } - @Test public void fromRunnable() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -175,7 +170,7 @@ public void disposedUpfront() { @Test public void cancelWhileRunning() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Maybe.fromRunnable(new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingleTest.java index 989bb78985..27c13626f6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,11 +24,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeFromSingleTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromSingleNull() { - Maybe.fromSingle(null); - } - @Test public void fromSingle() { Maybe.fromSingle(Single.just(1)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java index 9e90177fae..bcbe0783cc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeFromSupplierTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,11 +35,6 @@ public class MaybeFromSupplierTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void fromSupplierNull() { - Maybe.fromSupplier(null); - } - @Test public void fromSupplier() { final AtomicInteger atomicInteger = new AtomicInteger(); @@ -197,7 +192,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromSupplierObservable .subscribeOn(Schedulers.computation()) @@ -219,4 +214,23 @@ public String answer(InvocationOnMock invocation) throws Throwable { verify(observer).onSubscribe(any(Disposable.class)); verifyNoMoreInteractions(observer); } + + @Test + public void success() { + Maybe.fromSupplier(() -> 1) + .test() + .assertResult(1); + } + + @Test + public void disposeUpfront() throws Throwable { + @SuppressWarnings("unchecked") + Supplier supplier = mock(Supplier.class); + + Maybe.fromSupplier(supplier) + .test(true) + .assertEmpty(); + + verify(supplier, never()).get(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHideTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHideTest.java index 4428b57699..11a1cbb145 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHideTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeHideTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,7 +20,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.operators.ScalarSupplier; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.testsupport.TestHelper; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementTest.java index c41def19d3..a7ab2f2024 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIgnoreElementTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingleTest.java index 5a6021a48b..23ead59639 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptySingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptyTest.java index a7c208d2b5..a3a4a893c9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeIsEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJustTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJustTest.java index 6e61427a15..a49ac11a41 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJustTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeJustTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.ScalarSupplier; +import io.reactivex.rxjava3.operators.ScalarSupplier; public class MaybeJustTest extends RxJavaTest { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMapTest.java index 5259886976..b58b78b26f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterializeTest.java index 57a0ed11ae..0c7f570434 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMaterializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ public class MaybeMaterializeTest extends RxJavaTest { @Test - @SuppressWarnings("unchecked") public void success() { Maybe.just(1) .materialize() @@ -33,7 +32,6 @@ public void success() { } @Test - @SuppressWarnings("unchecked") public void error() { TestException ex = new TestException(); Maybe.error(ex) @@ -43,7 +41,6 @@ public void error() { } @Test - @SuppressWarnings("unchecked") public void empty() { Maybe.empty() .materialize() diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java index 5d74906294..bf4f7313dc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeArrayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,18 +21,18 @@ import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.maybe.MaybeMergeArray.MergeMaybeObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; public class MaybeMergeArrayTest extends RxJavaTest { - @SuppressWarnings("unchecked") @Test public void normal() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.SYNC); @@ -45,7 +45,6 @@ public void normal() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void fusedPollMixed() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); @@ -92,10 +91,9 @@ public void onComplete() { }); } - @SuppressWarnings("unchecked") @Test public void cancel() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Maybe.mergeArray(Maybe.just(1), Maybe.empty(), Maybe.just(2)) .subscribe(ts); @@ -106,10 +104,9 @@ public void cancel() { ts.assertEmpty(); } - @SuppressWarnings("unchecked") @Test public void firstErrors() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Maybe.mergeArray(Maybe.error(new TestException()), Maybe.empty(), Maybe.just(2)) .subscribe(ts); @@ -117,7 +114,6 @@ public void firstErrors() { ts.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorFused() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); @@ -130,7 +126,6 @@ public void errorFused() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -172,13 +167,12 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void mergeBadSource() { Maybe.mergeArray(new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(1); observer.onSuccess(2); observer.onSuccess(3); @@ -257,4 +251,25 @@ public void onComplete() { } }); } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported( + Maybe.mergeArray(MaybeSubject.create(), MaybeSubject.create()) + ); + } + + @Test + public void cancel2() { + TestHelper.checkDisposed(Maybe.mergeArray(MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void take() { + Maybe.mergeArray(Maybe.just(1), Maybe.empty(), Maybe.just(2)) + .doOnSubscribe(s -> s.request(Long.MAX_VALUE)) + .take(1) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeTest.java index 8ce37cb7be..0bf38ccc0f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeWithTest.java index ab4a0b8955..dd4d0817a8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeWithTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeMergeWithTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOfTypeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOfTypeTest.java index 0e459ffffb..5d58b9ff90 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOfTypeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOfTypeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorXTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorXTest.java index e7d5e4b31b..2cbc2fe427 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorXTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeOnErrorXTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -66,7 +66,6 @@ public void onErrorCompleteFalse() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void onErrorReturnFunctionThrows() { TestHelper.assertCompositeExceptions(Maybe.error(new TestException()) @@ -79,7 +78,6 @@ public Object apply(Throwable v) throws Exception { .to(TestHelper.testConsumer()), TestException.class, IOException.class); } - @SuppressWarnings("unchecked") @Test public void onErrorCompletePredicateThrows() { TestHelper.assertCompositeExceptions(Maybe.error(new TestException()) @@ -100,23 +98,6 @@ public void onErrorResumeNext() { .assertResult(1); } - @Test - public void onExceptionResumeNext() { - Maybe.error(new TestException()) - .onExceptionResumeNext(Maybe.just(1)) - .test() - .assertResult(1); - } - - @Test - public void onExceptionResumeNextPassthrough() { - Maybe.error(new AssertionError()) - .onExceptionResumeNext(Maybe.just(1)) - .test() - .assertFailure(AssertionError.class); - } - - @SuppressWarnings("unchecked") @Test public void onErrorResumeNextFunctionThrows() { TestHelper.assertCompositeExceptions(Maybe.error(new TestException()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeekTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeekTest.java index 2dce4c4524..ade9039eca 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeekTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybePeekTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -56,7 +56,7 @@ public void doubleError() { TestObserverEx to = new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -87,7 +87,7 @@ public void doubleComplete() { TestObserver to = new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onComplete(); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSafeSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSafeSubscribeTest.java new file mode 100644 index 0000000000..85919751d4 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSafeSubscribeTest.java @@ -0,0 +1,197 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import org.junit.Test; +import org.mockito.InOrder; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeSafeSubscribeTest { + + @Test + public void normalSuccess() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + + Maybe.just(1) + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onSuccess(1); + order.verifyNoMoreInteractions(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void normalError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + + Maybe.error(new TestException()) + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onError(any(TestException.class)); + order.verifyNoMoreInteractions(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void normalEmpty() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + + Maybe.empty() + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onComplete(); + order.verifyNoMoreInteractions(); + }); + } + + @Test + public void onSubscribeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + doThrow(new TestException()).when(consumer).onSubscribe(any()); + + Disposable d = Disposable.empty(); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(d); + // none of the following should arrive at the consumer + observer.onSuccess(1); + observer.onError(new IOException()); + observer.onComplete(); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verifyNoMoreInteractions(); + + assertTrue(d.isDisposed()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + TestHelper.assertUndeliverable(errors, 1, IOException.class); + }); + } + + @Test + public void onSuccessCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + doThrow(new TestException()).when(consumer).onSuccess(any()); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onSuccess(1); + order.verifyNoMoreInteractions(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void onErrorCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + doThrow(new TestException()).when(consumer).onError(any()); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + // none of the following should arrive at the consumer + observer.onError(new IOException()); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onError(any(IOException.class)); + order.verifyNoMoreInteractions(); + + TestHelper.assertError(errors, 0, CompositeException.class); + + CompositeException compositeException = (CompositeException)errors.get(0); + TestHelper.assertError(compositeException.getExceptions(), 0, IOException.class); + TestHelper.assertError(compositeException.getExceptions(), 1, TestException.class); + }); + } + + @Test + public void onCompleteCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + MaybeObserver consumer = mock(MaybeObserver.class); + doThrow(new TestException()).when(consumer).onComplete(); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + // none of the following should arrive at the consumer + observer.onComplete(); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onComplete(); + order.verifyNoMoreInteractions(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeStartWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeStartWithTest.java new file mode 100644 index 0000000000..31b0314158 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeStartWithTest.java @@ -0,0 +1,197 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class MaybeStartWithTest { + + @Test + public void justCompletableComplete() { + Maybe.just(1).startWith(Completable.complete()) + .test() + .assertResult(1); + } + + @Test + public void emptyCompletableComplete() { + Maybe.empty().startWith(Completable.complete()) + .test() + .assertResult(); + } + + @Test + public void runCompletableError() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run).startWith(Completable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justSingleJust() { + Maybe.just(1).startWith(Single.just(2)) + .test() + .assertResult(2, 1); + } + + @Test + public void emptySingleJust() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run) + .startWith(Single.just(2)) + .test() + .assertResult(2); + + verify(run).run(); + } + + @Test + public void runSingleError() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run).startWith(Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justMaybeJust() { + Maybe.just(1).startWith(Maybe.just(2)) + .test() + .assertResult(2, 1); + } + + @Test + public void emptyMaybeJust() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run) + .startWith(Maybe.just(2)) + .test() + .assertResult(2); + + verify(run).run(); + } + + @Test + public void runMaybeError() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run).startWith(Maybe.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justObservableJust() { + Maybe.just(1).startWith(Observable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5, 1); + } + + @Test + public void emptyObservableJust() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run) + .startWith(Observable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5); + + verify(run).run(); + } + + @Test + public void emptyObservableEmpty() { + Runnable run = mock(Runnable.class); + Runnable run2 = mock(Runnable.class); + + Maybe.fromRunnable(run) + .startWith(Observable.fromRunnable(run2)) + .test() + .assertResult(); + + verify(run).run(); + verify(run2).run(); + } + + @Test + public void runObservableError() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run).startWith(Observable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justFlowableJust() { + Maybe.just(1).startWith(Flowable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5, 1); + } + + @Test + public void emptyFlowableJust() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run) + .startWith(Flowable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5); + + verify(run).run(); + } + + @Test + public void emptyFlowableEmpty() { + Runnable run = mock(Runnable.class); + Runnable run2 = mock(Runnable.class); + + Maybe.fromRunnable(run) + .startWith(Flowable.fromRunnable(run2)) + .test() + .assertResult(); + + verify(run).run(); + verify(run2).run(); + } + + @Test + public void runFlowableError() { + Runnable run = mock(Runnable.class); + + Maybe.fromRunnable(run).startWith(Flowable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOnTest.java index 1043a848b8..3f5a29bc23 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingleTest.java index ceed4345d3..153b183287 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptySingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptyTest.java index 423d6ad1ac..f15f5ae8be 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchIfEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchOnNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchOnNextTest.java new file mode 100644 index 0000000000..7a502faa28 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeSwitchOnNextTest.java @@ -0,0 +1,123 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; + +public class MaybeSwitchOnNextTest extends RxJavaTest { + + @Test + public void normal() { + Maybe.switchOnNext( + Flowable.range(1, 10) + .map(v -> { + if (v % 2 == 0) { + return Maybe.just(v); + } + return Maybe.empty(); + }) + ) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void normalDelayError() { + Maybe.switchOnNextDelayError( + Flowable.range(1, 10) + .map(v -> { + if (v % 2 == 0) { + return Maybe.just(v); + } + return Maybe.empty(); + }) + ) + .test() + .assertResult(2, 4, 6, 8, 10); + } + + @Test + public void noDelaySwitch() { + PublishProcessor> pp = PublishProcessor.create(); + + TestSubscriber ts = Maybe.switchOnNext(pp).test(); + + assertTrue(pp.hasSubscribers()); + + ts.assertEmpty(); + + MaybeSubject ms1 = MaybeSubject.create(); + MaybeSubject ms2 = MaybeSubject.create(); + + pp.onNext(ms1); + + assertTrue(ms1.hasObservers()); + + pp.onNext(ms2); + + assertFalse(ms1.hasObservers()); + assertTrue(ms2.hasObservers()); + + pp.onComplete(); + + assertTrue(ms2.hasObservers()); + + ms2.onSuccess(1); + + ts.assertResult(1); + } + + @Test + public void delaySwitch() { + PublishProcessor> pp = PublishProcessor.create(); + + TestSubscriber ts = Maybe.switchOnNextDelayError(pp).test(); + + assertTrue(pp.hasSubscribers()); + + ts.assertEmpty(); + + MaybeSubject ms1 = MaybeSubject.create(); + MaybeSubject ms2 = MaybeSubject.create(); + + pp.onNext(ms1); + + assertTrue(ms1.hasObservers()); + + pp.onNext(ms2); + + assertFalse(ms1.hasObservers()); + assertTrue(ms2.hasObservers()); + + assertTrue(ms2.hasObservers()); + + ms2.onError(new TestException()); + + assertTrue(pp.hasSubscribers()); + + ts.assertEmpty(); + + pp.onComplete(); + + ts.assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisherTest.java index e615c733e8..f5ce249514 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilTest.java index 5c7d8f956d..20ce1cfa41 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTakeUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeIntervalTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeIntervalTest.java new file mode 100644 index 0000000000..bd50202ae4 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeIntervalTest.java @@ -0,0 +1,111 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.*; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeTimeIntervalTest { + + @Test + public void just() { + Maybe.just(1) + .timeInterval() + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void empty() { + Maybe.empty() + .timeInterval() + .test() + .assertResult(); + } + + @Test + public void error() { + Maybe.error(new TestException()) + .timeInterval() + .test() + .assertFailure(TestException.class); + } + + @Test + public void justSeconds() { + Maybe.just(1) + .timeInterval(TimeUnit.SECONDS) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justScheduler() { + Maybe.just(1) + .timeInterval(Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justSecondsScheduler() { + Maybe.just(1) + .timeInterval(TimeUnit.SECONDS, Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(m -> m.timeInterval()); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(MaybeSubject.create().timeInterval()); + } + + @Test + public void timeInfo() { + TestScheduler scheduler = new TestScheduler(); + + MaybeSubject ms = MaybeSubject.create(); + + TestObserver> to = ms + .timeInterval(scheduler) + .test(); + + scheduler.advanceTimeBy(1000, TimeUnit.MILLISECONDS); + + ms.onSuccess(1); + + to.assertResult(new Timed<>(1, 1000L, TimeUnit.MILLISECONDS)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java index 188db93597..efa2ce6ff6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,9 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.observers.TestObserver; @@ -231,4 +233,29 @@ public Object apply(Flowable f) throws Exception { } }, false, null, 1, 1); } + + @Test + public void mainSuccessAfterOtherSignal() { + PublishProcessor pp = PublishProcessor.create(); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + pp.onNext(2); + observer.onSuccess(1); + } + } + .timeout(pp) + .test() + .assertFailure(TimeoutException.class); + } + + @Test + public void mainSuccess() { + Maybe.just(1) + .timeout(Flowable.never()) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java index 7e30ce6a49..b5b61a6ffc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimeoutTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,12 +19,15 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.testsupport.*; public class MaybeTimeoutTest extends RxJavaTest { @@ -341,4 +344,21 @@ public void run() { } } } + + @Test + public void mainSuccessAfterOtherSignal() { + MaybeSubject ms = MaybeSubject.create(); + + new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + observer.onSubscribe(Disposable.empty()); + ms.onSuccess(2); + observer.onSuccess(1); + } + } + .timeout(ms) + .test() + .assertFailure(TimeoutException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimerTest.java index 06a54ad90c..05ffdf0562 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public void dispose() { public void timerInterruptible() throws Exception { ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); try { - for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec) }) { + for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec, true) }) { final AtomicBoolean interrupted = new AtomicBoolean(); TestObserver to = Maybe.timer(1, TimeUnit.MILLISECONDS, s) .map(new Function() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimestampTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimestampTest.java new file mode 100644 index 0000000000..935147a906 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeTimestampTest.java @@ -0,0 +1,111 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.*; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class MaybeTimestampTest { + + @Test + public void just() { + Maybe.just(1) + .timestamp() + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void empty() { + Maybe.empty() + .timestamp() + .test() + .assertResult(); + } + + @Test + public void error() { + Maybe.error(new TestException()) + .timestamp() + .test() + .assertFailure(TestException.class); + } + + @Test + public void justSeconds() { + Maybe.just(1) + .timestamp(TimeUnit.SECONDS) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justScheduler() { + Maybe.just(1) + .timestamp(Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justSecondsScheduler() { + Maybe.just(1) + .timestamp(TimeUnit.SECONDS, Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeMaybe(m -> m.timestamp()); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(MaybeSubject.create().timestamp()); + } + + @Test + public void timeInfo() { + TestScheduler scheduler = new TestScheduler(); + + MaybeSubject ms = MaybeSubject.create(); + + TestObserver> to = ms + .timestamp(scheduler) + .test(); + + scheduler.advanceTimeBy(1000, TimeUnit.MILLISECONDS); + + ms.onSuccess(1); + + to.assertResult(new Timed<>(1, 1000L, TimeUnit.MILLISECONDS)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToCompletableTest.java index 52b44e5f98..4bae2cc98f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowableTest.java index 77d098c0ce..66e94da8d0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFutureTest.java new file mode 100644 index 0000000000..b36a299cd4 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToFutureTest.java @@ -0,0 +1,84 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.maybe; + +import static org.junit.Assert.*; + +import java.util.concurrent.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.MaybeSubject; + +public class MaybeToFutureTest extends RxJavaTest { + + @Test + public void success() throws Exception { + assertEquals((Integer)1, Maybe.just(1) + .subscribeOn(Schedulers.computation()) + .toFuture() + .get()); + } + + @Test + public void empty() throws Exception { + assertNull(Maybe.empty() + .subscribeOn(Schedulers.computation()) + .toFuture() + .get()); + } + + @Test + public void error() throws InterruptedException { + try { + Maybe.error(new TestException()) + .subscribeOn(Schedulers.computation()) + .toFuture() + .get(); + + fail("Should have thrown!"); + } catch (ExecutionException ex) { + assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException); + } + } + + @Test + public void cancel() { + MaybeSubject ms = MaybeSubject.create(); + + Future f = ms.toFuture(); + + assertTrue(ms.hasObservers()); + + f.cancel(true); + + assertFalse(ms.hasObservers()); + } + + @Test + public void cancel2() { + MaybeSubject ms = MaybeSubject.create(); + + Future f = ms.toFuture(); + + assertTrue(ms.hasObservers()); + + f.cancel(false); + + assertFalse(ms.hasObservers()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservableTest.java index c201bf20f8..6747203723 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingleTest.java index 9d641c9505..1ee84470f2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeToSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOnTest.java index 1e2d700136..3159fb411e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUnsubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java index 79c81bf9a5..0f083c93d2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeUsingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -411,11 +411,11 @@ public MaybeSource apply(Object v) throws Exception { return Maybe.wrap(new MaybeSource() { @Override public void subscribe(MaybeObserver observer) { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -481,6 +481,7 @@ public void run() { } @Test + @SuppressUndeliverable public void errorDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java index aea7a478ce..2cbf8e4263 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipArrayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,17 +15,21 @@ import static org.junit.Assert.*; -import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeZipArrayTest extends RxJavaTest { @@ -151,7 +155,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipArrayOneIsNull() { Maybe.zipArray(new Function() { @@ -163,11 +166,76 @@ public Object apply(Object[] v) { .blockingGet(); } - @SuppressWarnings("unchecked") @Test public void singleSourceZipperReturnsNull() { Maybe.zipArray(Functions.justFunction(null), Maybe.just(1)) .to(TestHelper.testConsumer()) .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Maybe.zipArray(v -> v, MaybeSubject.create(), MaybeSubject.create())); + } + + @Test + public void bothComplete() { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Maybe m1 = new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + ref1.set(observer); + } + }; + Maybe m2 = new Maybe() { + @Override + protected void subscribeActual(@NonNull MaybeObserver observer) { + ref2.set(observer); + } + }; + + TestObserver to = Maybe.zipArray(v -> v, m1, m2) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref2.get().onSubscribe(Disposable.empty()); + + ref1.get().onComplete(); + ref2.get().onComplete(); + + to.assertResult(); + } + + @Test + public void bothSucceed() { + Maybe.zipArray(v -> Arrays.asList(v), Maybe.just(1), Maybe.just(2)) + .test() + .assertResult(Arrays.asList(1, 2)); + } + + @Test + public void oneSourceOnly() { + Maybe.zipArray(v -> Arrays.asList(v), Maybe.just(1)) + .test() + .assertResult(Arrays.asList(1)); + } + + @Test + public void onSuccessAfterDispose() { + AtomicReference> emitter = new AtomicReference<>(); + + TestObserver> to = Maybe.zipArray(Arrays::asList, + (MaybeSource)o -> emitter.set(o), Maybe.never()) + .test(); + + emitter.get().onSubscribe(Disposable.empty()); + + to.dispose(); + + emitter.get().onSuccess(1); + + to.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterableTest.java index 9ce40cee25..2d60c22157 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/maybe/MaybeZipIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,6 @@ public Object apply(Object[] a) throws Exception { } }; - @SuppressWarnings("unchecked") @Test public void firstError() { Maybe.zip(Arrays.asList(Maybe.error(new TestException()), Maybe.just(1)), addString) @@ -46,7 +45,6 @@ public void firstError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void secondError() { Maybe.zip(Arrays.asList(Maybe.just(1), Maybe.error(new TestException())), addString) @@ -54,7 +52,6 @@ public void secondError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void dispose() { PublishProcessor pp = PublishProcessor.create(); @@ -69,7 +66,6 @@ public void dispose() { assertFalse(pp.hasSubscribers()); } - @SuppressWarnings("unchecked") @Test public void zipperThrows() { Maybe.zip(Arrays.asList(Maybe.just(1), Maybe.just(2)), new Function() { @@ -82,7 +78,6 @@ public Object apply(Object[] b) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void zipperReturnsNull() { Maybe.zip(Arrays.asList(Maybe.just(1), Maybe.just(2)), new Function() { @@ -95,7 +90,6 @@ public Object apply(Object[] a) throws Exception { .assertFailure(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void middleError() { PublishProcessor pp0 = PublishProcessor.create(); @@ -112,7 +106,6 @@ public void middleError() { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void innerErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -156,7 +149,7 @@ public void run() { @Test public void iteratorThrows() { - Maybe.zip(new CrashingMappedIterable>(1, 100, 100, new Function>() { + Maybe.zip(new CrashingMappedIterable<>(1, 100, 100, new Function>() { @Override public Maybe apply(Integer v) throws Exception { return Maybe.just(v); @@ -168,7 +161,7 @@ public Maybe apply(Integer v) throws Exception { @Test public void hasNextThrows() { - Maybe.zip(new CrashingMappedIterable>(100, 20, 100, new Function>() { + Maybe.zip(new CrashingMappedIterable<>(100, 20, 100, new Function>() { @Override public Maybe apply(Integer v) throws Exception { return Maybe.just(v); @@ -180,7 +173,7 @@ public Maybe apply(Integer v) throws Exception { @Test public void nextThrows() { - Maybe.zip(new CrashingMappedIterable>(100, 100, 5, new Function>() { + Maybe.zip(new CrashingMappedIterable<>(100, 100, 5, new Function>() { @Override public Maybe apply(Integer v) throws Exception { return Maybe.just(v); @@ -190,7 +183,6 @@ public Maybe apply(Integer v) throws Exception { .assertFailureAndMessage(TestException.class, "next()"); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableOneIsNull() { Maybe.zip(Arrays.asList(null, Maybe.just(1)), new Function() { @@ -202,7 +194,6 @@ public Object apply(Object[] v) { .blockingGet(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableTwoIsNull() { Maybe.zip(Arrays.asList(Maybe.just(1), null), new Function() { @@ -214,7 +205,6 @@ public Object apply(Object[] v) { .blockingGet(); } - @SuppressWarnings("unchecked") @Test public void singleSourceZipperReturnsNull() { Maybe.zipArray(Functions.justFunction(null), Maybe.just(1)) @@ -222,7 +212,6 @@ public void singleSourceZipperReturnsNull() { .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); } - @SuppressWarnings("unchecked") @Test public void maybeSourcesInIterable() { MaybeSource source = new MaybeSource() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservableTest.java index 9c8dfa8754..a1e087bdf2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisherTest.java index 9c0a498db5..bd9c9aa2d9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/CompletableAndThenPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java index 28c90619bb..44acad42b1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subjects.CompletableSubject; import io.reactivex.rxjava3.testsupport.*; @@ -66,6 +66,14 @@ public void simpleLongPrefetch() { .assertResult(); } + @Test + public void simpleLongPrefetchHidden() { + Flowable.range(1, 1024).hide() + .concatMapCompletable(Functions.justFunction(Completable.complete()), 32) + .test() + .assertResult(); + } + @Test public void mainError() { Flowable.error(new TestException()) @@ -277,7 +285,7 @@ protected void subscribeActual(Subscriber s) { Functions.justFunction(Completable.never()), 1 ) .test() - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { @@ -431,4 +439,54 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Flowable.range(1, 5).hide() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicSyncFused() { + Flowable.range(1, 5) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicAsyncFused() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectFlowableFusion() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Flowable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java index 488a803172..12b61a5e8c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -31,7 +31,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.MaybeSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -54,15 +54,19 @@ public MaybeSource apply(Integer v) } @Test - public void simpleLong() { + public void simpleLongPrefetch() { Flowable.range(1, 1024) - .concatMapMaybe(new Function>() { - @Override - public MaybeSource apply(Integer v) - throws Exception { - return Maybe.just(v); - } - }, 32) + .concatMapMaybe(Maybe::just, 32) + .test() + .assertValueCount(1024) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void simpleLongPrefetchHidden() { + Flowable.range(1, 1024).hide() + .concatMapMaybe(Maybe::just, 32) .test() .assertValueCount(1024) .assertNoErrors() @@ -245,7 +249,7 @@ protected void subscribeActual(Subscriber s) { Functions.justFunction(Maybe.never()), 1 ) .test() - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { @@ -291,7 +295,7 @@ public void innerErrorAfterMainError() { try { final PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> obs = new AtomicReference>(); + final AtomicReference> obs = new AtomicReference<>(); TestSubscriberEx ts = pp.concatMapMaybe( new Function>() { @@ -302,7 +306,7 @@ public MaybeSource apply(Integer v) @Override protected void subscribeActual( MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); obs.set(observer); } }; @@ -368,9 +372,9 @@ public MaybeSource apply(Integer v) @Test public void cancelNoConcurrentClean() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ConcatMapMaybeSubscriber operator = - new ConcatMapMaybeSubscriber( + new ConcatMapMaybeSubscriber<>( ts, Functions.justFunction(Maybe.never()), 16, ErrorMode.IMMEDIATE); operator.onSubscribe(new BooleanSubscription()); @@ -464,4 +468,54 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Flowable.range(1, 5).hide() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Flowable.range(1, 5) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectFlowableFusion() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Flowable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java index 359053cdb5..60cb2f5de0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableConcatMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,11 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ErrorMode; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.subjects.SingleSubject; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -52,15 +52,19 @@ public SingleSource apply(Integer v) } @Test - public void simpleLong() { + public void simpleLongPrefetch() { Flowable.range(1, 1024) - .concatMapSingle(new Function>() { - @Override - public SingleSource apply(Integer v) - throws Exception { - return Single.just(v); - } - }, 32) + .concatMapSingle(Single::just, 32) + .test() + .assertValueCount(1024) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void simpleLongPrefetchHidden() { + Flowable.range(1, 1024).hide() + .concatMapSingle(Single::just, 32) .test() .assertValueCount(1024) .assertNoErrors() @@ -163,7 +167,7 @@ protected void subscribeActual(Subscriber s) { Functions.justFunction(Single.never()), 1 ) .test() - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { @@ -209,7 +213,7 @@ public void innerErrorAfterMainError() { try { final PublishProcessor pp = PublishProcessor.create(); - final AtomicReference> obs = new AtomicReference>(); + final AtomicReference> obs = new AtomicReference<>(); TestSubscriberEx ts = pp.concatMapSingle( new Function>() { @@ -220,7 +224,7 @@ public SingleSource apply(Integer v) @Override protected void subscribeActual( SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); obs.set(observer); } }; @@ -286,9 +290,9 @@ public SingleSource apply(Integer v) @Test public void cancelNoConcurrentClean() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ConcatMapSingleSubscriber operator = - new ConcatMapSingleSubscriber( + new ConcatMapSingleSubscriber<>( ts, Functions.justFunction(Single.never()), 16, ErrorMode.IMMEDIATE); operator.onSubscribe(new BooleanSubscription()); @@ -382,4 +386,54 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Flowable.range(1, 5).hide() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Flowable.range(1, 5) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastProcessor up = UnicastProcessor.create(); + TestHelper.emit(up, 1, 2, 3, 4, 5); + + up + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectFlowableFusion() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Flowable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.flowableStripBoundary()) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletableTest.java index 5cd60792a7..cf2065cc72 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -174,7 +174,7 @@ public CompletableSource apply(Integer f) throws Exception { @Test public void mapperCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Flowable.range(1, 5).switchMapCompletable(new Function() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybeTest.java index 1eea34f3fa..1ace07440f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,11 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -307,7 +307,7 @@ public MaybeSource apply(Integer v) @Test public void disposeBeforeSwitchInOnNext() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1) .switchMapMaybe(new Function>() { @@ -324,7 +324,7 @@ public MaybeSource apply(Integer v) @Test public void disposeOnNextAfterFirst() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2) .switchMapMaybe(new Function>() { @@ -404,7 +404,7 @@ public MaybeSource apply(Integer v) public void innerErrorAfterTermination() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> moRef = new AtomicReference>(); + final AtomicReference> moRef = new AtomicReference<>(); TestSubscriberEx ts = new Flowable() { @Override @@ -422,7 +422,7 @@ public MaybeSource apply(Integer v) @Override protected void subscribeActual( MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); moRef.set(observer); } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingleTest.java index a834f327f9..cd4d64a9eb 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/FlowableSwitchMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,11 +18,11 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -255,7 +255,7 @@ public SingleSource apply(Integer v) @Test public void disposeBeforeSwitchInOnNext() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1) .switchMapSingle(new Function>() { @@ -272,7 +272,7 @@ public SingleSource apply(Integer v) @Test public void disposeOnNextAfterFirst() { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2) .switchMapSingle(new Function>() { @@ -352,7 +352,7 @@ public SingleSource apply(Integer v) public void innerErrorAfterTermination() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> moRef = new AtomicReference>(); + final AtomicReference> moRef = new AtomicReference<>(); TestSubscriberEx ts = new Flowable() { @Override @@ -370,7 +370,7 @@ public SingleSource apply(Integer v) @Override protected void subscribeActual( SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); moRef.set(observer); } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservableTest.java index 0b268e8d88..f91b5c6582 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisherTest.java index b7853ccac0..96948b05dc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/MaybeFlatMapPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java index 540e0828af..e69e3e0ee5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -474,4 +474,54 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Observable.range(1, 5).hide() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicSyncFused() { + Observable.range(1, 5) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicAsyncFused() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertResult(); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectObservableFusion() + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Observable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + .concatMapCompletable(v -> Completable.complete().hide()) + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java index 30f78c41a9..0dcbd27408 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -239,7 +239,7 @@ public void mainErrorAfterInnerError() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("outer")); } @@ -262,7 +262,7 @@ public void innerErrorAfterMainError() { try { final PublishSubject ps = PublishSubject.create(); - final AtomicReference> obs = new AtomicReference>(); + final AtomicReference> obs = new AtomicReference<>(); TestObserverEx to = ps.concatMapMaybe( new Function>() { @@ -273,7 +273,7 @@ public MaybeSource apply(Integer v) @Override protected void subscribeActual( MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); obs.set(observer); } }; @@ -373,12 +373,12 @@ public void scalarEmptySource() { @Test public void cancelNoConcurrentClean() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); ConcatMapMaybeMainObserver operator = - new ConcatMapMaybeMainObserver( + new ConcatMapMaybeMainObserver<>( to, Functions.justFunction(Maybe.never()), 16, ErrorMode.IMMEDIATE); - operator.onSubscribe(Disposables.empty()); + operator.onSubscribe(Disposable.empty()); operator.queue.offer(1); @@ -399,7 +399,6 @@ public void cancelNoConcurrentClean() { public void checkUnboundedInnerQueue() { MaybeSubject ms = MaybeSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable .fromArray(ms, Maybe.just(2), Maybe.just(3), Maybe.just(4)) .concatMapMaybe(Functions.>identity(), 2) @@ -486,4 +485,54 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Observable.range(1, 5).hide() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Observable.range(1, 5) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectObservableFusion() + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Observable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + .concatMapMaybe(v -> Maybe.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java index 2d4b46f187..9f70b4addf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableConcatMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -157,7 +157,7 @@ public void mainErrorAfterInnerError() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("outer")); } @@ -180,7 +180,7 @@ public void innerErrorAfterMainError() { try { final PublishSubject ps = PublishSubject.create(); - final AtomicReference> obs = new AtomicReference>(); + final AtomicReference> obs = new AtomicReference<>(); TestObserverEx to = ps.concatMapSingle( new Function>() { @@ -191,7 +191,7 @@ public SingleSource apply(Integer v) @Override protected void subscribeActual( SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); obs.set(observer); } }; @@ -313,12 +313,12 @@ public void scalarEmptySource() { @Test public void cancelNoConcurrentClean() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); ConcatMapSingleMainObserver operator = - new ConcatMapSingleMainObserver( + new ConcatMapSingleMainObserver<>( to, Functions.justFunction(Single.never()), 16, ErrorMode.IMMEDIATE); - operator.onSubscribe(Disposables.empty()); + operator.onSubscribe(Disposable.empty()); operator.queue.offer(1); @@ -339,7 +339,6 @@ public void cancelNoConcurrentClean() { public void checkUnboundedInnerQueue() { SingleSubject ss = SingleSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable .fromArray(ss, Single.just(2), Single.just(3), Single.just(4)) .concatMapSingle(Functions.>identity(), 2) @@ -426,4 +425,54 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void basicNonFused() { + Observable.range(1, 5).hide() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicSyncFused() { + Observable.range(1, 5) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicAsyncFused() { + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); + + us + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertResult(1, 2, 3, 4, 5); + } + + @Test + public void basicFusionRejected() { + TestHelper.rejectObservableFusion() + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertEmpty(); + } + + @Test + public void fusedPollCrash() { + Observable.range(1, 5) + .map(v -> { + if (v == 3) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + .concatMapSingle(v -> Single.just(v).hide()) + .test() + .assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletableTest.java index b308119424..0efeb6f676 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -172,7 +172,7 @@ public CompletableSource apply(Integer f) throws Exception { @Test public void mapperCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5).switchMapCompletable(new Function() { @Override @@ -314,7 +314,7 @@ public void innerErrorThenMainError() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("main")); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybeTest.java index c9a1f9a0dd..b2eabdd96f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -283,7 +283,7 @@ public MaybeSource apply(Integer v) @Test public void disposeBeforeSwitchInOnNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1) .switchMapMaybe(new Function>() { @@ -300,7 +300,7 @@ public MaybeSource apply(Integer v) @Test public void disposeOnNextAfterFirst() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1, 2) .switchMapMaybe(new Function>() { @@ -355,7 +355,7 @@ public void mainErrorAfterTermination() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("outer")); } @@ -380,12 +380,12 @@ public MaybeSource apply(Integer v) public void innerErrorAfterTermination() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> moRef = new AtomicReference>(); + final AtomicReference> moRef = new AtomicReference<>(); TestObserverEx to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("outer")); } @@ -398,7 +398,7 @@ public MaybeSource apply(Integer v) @Override protected void subscribeActual( MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); moRef.set(observer); } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingleTest.java index a59e9954ec..a98afae4de 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ObservableSwitchMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -252,7 +252,7 @@ public SingleSource apply(Integer v) @Test public void disposeBeforeSwitchInOnNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1).hide() .switchMapSingle(new Function>() { @@ -269,7 +269,7 @@ public SingleSource apply(Integer v) @Test public void disposeOnNextAfterFirst() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1, 2) .switchMapSingle(new Function>() { @@ -324,7 +324,7 @@ public void mainErrorAfterTermination() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("outer")); } @@ -349,12 +349,12 @@ public SingleSource apply(Integer v) public void innerErrorAfterTermination() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> moRef = new AtomicReference>(); + final AtomicReference> moRef = new AtomicReference<>(); TestObserverEx to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException("outer")); } @@ -367,7 +367,7 @@ public SingleSource apply(Integer v) @Override protected void subscribeActual( SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); moRef.set(observer); } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelperTest.java index 8a8e745c8b..2ac6eed7d3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/ScalarXMapZHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservableTest.java index 3f9fcb74de..a002620b6a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/mixed/SingleFlatMapObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstreamTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstreamTest.java index b578dcb8f6..5d49cd8b66 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstreamTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/AbstractObservableWithUpstreamTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatestTest.java index ef8e23ba21..feac576be7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecentTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecentTest.java index 6ffd920f24..ccf611bbd4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecentTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableMostRecentTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,10 +27,6 @@ import io.reactivex.rxjava3.subjects.*; public class BlockingObservableMostRecentTest extends RxJavaTest { - @Test - public void mostRecentNull() { - assertNull(Observable.never().blockingMostRecent(null).iterator().next()); - } static Iterable mostRecent(Observable source, T initialValue) { return source.blockingMostRecent(initialValue); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java index 194619b8bc..8274e9f9e3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableNextTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -63,7 +63,7 @@ public void run() { } static Iterable next(ObservableSource source) { - return new BlockingObservableNext(source); + return new BlockingObservableNext<>(source); } @Test @@ -227,8 +227,8 @@ public void nextWithCallingHasNextMultipleTimes() { /** * Confirm that no buffering or blocking of the Observable onNext calls occurs and it just grabs the next emitted value. - *

    - * This results in output such as => a: 1 b: 2 c: 89 + *

    + * This results in output such as {@code => a: 1 b: 2 c: 89} * * @throws Throwable some method call is declared throws */ @@ -247,7 +247,7 @@ public void noBufferingOrBlockingOfSequence() throws Throwable { @Override public void subscribe(final Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); task.replace(Schedulers.single().scheduleDirect(new Runnable() { @Override @@ -356,7 +356,7 @@ public void remove() { @Test public void nextObserverError() { - NextObserver no = new NextObserver(); + NextObserver no = new NextObserver<>(); List errors = TestHelper.trackPluginErrors(); try { @@ -370,7 +370,7 @@ public void nextObserverError() { @Test public void nextObserverOnNext() throws Exception { - NextObserver no = new NextObserver(); + NextObserver no = new NextObserver<>(); no.setWaiting(); no.onNext(Notification.createOnNext(1)); @@ -383,7 +383,7 @@ public void nextObserverOnNext() throws Exception { @Test public void nextObserverOnCompleteOnNext() throws Exception { - NextObserver no = new NextObserver(); + NextObserver no = new NextObserver<>(); no.setWaiting(); no.onNext(Notification.createOnComplete()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToFutureTest.java index 756f0b4eb3..dacf971596 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToFutureTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToFutureTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.observable; import static org.junit.Assert.*; @@ -20,12 +18,12 @@ import java.util.*; import java.util.concurrent.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; public class BlockingObservableToFutureTest extends RxJavaTest { @@ -65,7 +63,7 @@ public void toFutureWithException() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("one"); observer.onError(new TestException()); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java index 3a98208d70..684eefbf61 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/BlockingObservableToIteratorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.operators.observable.BlockingObservableIterable.BlockingObservableIterator; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -56,7 +56,7 @@ public void toIteratorWithException() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("one"); observer.onError(new TestException()); } @@ -73,7 +73,7 @@ public void subscribe(Observer observer) { @Test public void dispose() { - BlockingObservableIterator it = new BlockingObservableIterator(128); + BlockingObservableIterator it = new BlockingObservableIterator<>(128); assertFalse(it.isDisposed()); @@ -84,7 +84,7 @@ public void dispose() { @Test public void interruptWait() { - BlockingObservableIterator it = new BlockingObservableIterator(128); + BlockingObservableIterator it = new BlockingObservableIterator<>(128); try { Thread.currentThread().interrupt(); @@ -97,14 +97,14 @@ public void interruptWait() { @Test(expected = NoSuchElementException.class) public void emptyThrowsNoSuch() { - BlockingObservableIterator it = new BlockingObservableIterator(128); + BlockingObservableIterator it = new BlockingObservableIterator<>(128); it.onComplete(); it.next(); } @Test(expected = UnsupportedOperationException.class) public void remove() { - BlockingObservableIterator it = new BlockingObservableIterator(128); + BlockingObservableIterator it = new BlockingObservableIterator<>(128); it.remove(); } @@ -131,4 +131,13 @@ public void run() { assertFalse(it.hasNext()); } + + @Test(expected = TestException.class) + public void errorAfterDispose() { + Iterator it = Observable.error(new TestException()).blockingIterable().iterator(); + + ((Disposable)it).dispose(); + + it.hasNext(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/Burst.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/Burst.java index 4fa76d1628..46b5df42f3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/Burst.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/Burst.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,13 +10,14 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import java.util.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; /** * Creates {@link Observable} of a number of items followed by either an error or @@ -36,7 +37,7 @@ public final class Burst extends Observable { @Override protected void subscribeActual(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); for (T item: items) { observer.onNext(item); } @@ -47,13 +48,13 @@ protected void subscribeActual(final Observer observer) { } } - @SuppressWarnings("unchecked") public static Builder item(T item) { return items(item); } + @SafeVarargs public static Builder items(T... items) { - return new Builder(Arrays.asList(items)); + return new Builder<>(Arrays.asList(items)); } public static final class Builder { @@ -71,7 +72,7 @@ public Observable error(Throwable e) { } public Observable create() { - return new Burst(error, items); + return new Burst<>(error, items); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java index 5934706a4d..fba37cb9f9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAllTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -146,7 +146,7 @@ public Observable apply(Boolean t1) { @Test public void predicateThrowsExceptionAndValueInCauseMessageObservable() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); @@ -277,7 +277,7 @@ public Observable apply(Boolean t1) { @Test public void predicateThrowsExceptionAndValueInCauseMessage() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); @@ -311,7 +311,7 @@ public void predicateThrowsObservable() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); @@ -342,7 +342,7 @@ public void predicateThrows() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); @@ -364,4 +364,14 @@ public boolean test(Integer v) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservableToSingle(o -> o.all(v -> true)); + } + + @Test + public void doubleOnSubscribeObservable() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.all(v -> true).toObservable()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmbTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmbTest.java index 14c720257a..5ec9129d9f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmbTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAmbTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -92,7 +92,6 @@ public void amb() { Observable observable3 = createObservable(new String[] { "3", "33", "333", "3333" }, 3000, null); - @SuppressWarnings("unchecked") Observable o = Observable.ambArray(observable1, observable2, observable3); @@ -121,7 +120,6 @@ public void amb2() { Observable observable3 = createObservable(new String[] {}, 3000, new IOException("fake exception")); - @SuppressWarnings("unchecked") Observable o = Observable.ambArray(observable1, observable2, observable3); @@ -148,7 +146,6 @@ public void amb3() { Observable observable3 = createObservable(new String[] { "3" }, 3000, null); - @SuppressWarnings("unchecked") Observable o = Observable.ambArray(observable1, observable2, observable3); @@ -161,7 +158,6 @@ public void amb3() { inOrder.verifyNoMoreInteractions(); } - @SuppressWarnings("unchecked") @Test public void subscriptionOnlyHappensOnce() throws InterruptedException { final AtomicLong count = new AtomicLong(); @@ -178,7 +174,7 @@ public void accept(Disposable d) { //this stream emits second Observable o2 = Observable.just(1).doOnSubscribe(incrementer) .delay(100, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.computation()); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.ambArray(o1, o2).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -205,14 +201,13 @@ public void accept(Integer t) { assertEquals(1, result); } - @SuppressWarnings("unchecked") @Test public void ambCancelsOthers() { PublishSubject source1 = PublishSubject.create(); PublishSubject source2 = PublishSubject.create(); PublishSubject source3 = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.ambArray(source1, source2, source3).subscribe(to); @@ -228,13 +223,11 @@ public void ambCancelsOthers() { } - @SuppressWarnings("unchecked") @Test public void ambArrayEmpty() { assertSame(Observable.empty(), Observable.ambArray()); } - @SuppressWarnings("unchecked") @Test public void ambArraySingleElement() { assertSame(Observable.never(), Observable.ambArray(Observable.never())); @@ -242,7 +235,8 @@ public void ambArraySingleElement() { @Test public void manySources() { - Observable[] a = new Observable[32]; + @SuppressWarnings("unchecked") + Observable[] a = new Observable[32]; Arrays.fill(a, Observable.never()); a[31] = Observable.just(1); @@ -265,7 +259,6 @@ public void singleIterable() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void disposed() { TestHelper.checkDisposed(Observable.ambArray(Observable.never(), Observable.never())); @@ -277,7 +270,6 @@ public void onNextRace() { final PublishSubject ps1 = PublishSubject.create(); final PublishSubject ps2 = PublishSubject.create(); - @SuppressWarnings("unchecked") TestObserverEx to = Observable.ambArray(ps1, ps2).to(TestHelper.testConsumer()); Runnable r1 = new Runnable() { @@ -308,7 +300,6 @@ public void onCompleteRace() { final PublishSubject ps1 = PublishSubject.create(); final PublishSubject ps2 = PublishSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable.ambArray(ps1, ps2).test(); Runnable r1 = new Runnable() { @@ -336,7 +327,6 @@ public void onErrorRace() { final PublishSubject ps1 = PublishSubject.create(); final PublishSubject ps2 = PublishSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable.ambArray(ps1, ps2).test(); final Throwable ex = new TestException(); @@ -374,21 +364,18 @@ public void ambWithOrder() { Observable.just(1).ambWith(error).test().assertValue(1).assertComplete(); } - @SuppressWarnings("unchecked") @Test public void ambIterableOrder() { Observable error = Observable.error(new RuntimeException()); Observable.amb(Arrays.asList(Observable.just(1), error)).test().assertValue(1).assertComplete(); } - @SuppressWarnings("unchecked") @Test public void ambArrayOrder() { Observable error = Observable.error(new RuntimeException()); Observable.ambArray(Observable.just(1), error).test().assertValue(1).assertComplete(); } - @SuppressWarnings("unchecked") @Test public void noWinnerSuccessDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -414,7 +401,6 @@ public void accept(Object v) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerErrorDispose() throws Exception { final TestException ex = new TestException(); @@ -441,7 +427,6 @@ public void accept(Throwable e) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerCompleteDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -467,7 +452,6 @@ public void run() throws Exception { } } - @SuppressWarnings("unchecked") @Test public void observableSourcesInIterable() { ObservableSource source = new ObservableSource() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnyTest.java index a79479067a..cb85603fa1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAnyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,10 +21,10 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -247,7 +247,7 @@ public Observable apply(Boolean t1) { @Test public void predicateThrowsExceptionAndValueInCauseMessageObservable() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); Observable.just("Boo!").any(new Predicate() { @@ -469,7 +469,7 @@ public Observable apply(Boolean t1) { @Test public void predicateThrowsExceptionAndValueInCauseMessage() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final IllegalArgumentException ex = new IllegalArgumentException(); Observable.just("Boo!").any(new Predicate() { @@ -517,7 +517,7 @@ public void predicateThrowsSuppressOthers() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); @@ -548,7 +548,7 @@ public void badSourceSingle() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onNext(1); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnectTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnectTest.java index 5ed5b3124e..16128ff195 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnectTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableAutoConnectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingTest.java index d1c6c6fc51..05fbb5a94c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBlockingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,7 +48,7 @@ public void blockingFirstDefault() { @Test public void blockingSubscribeConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -64,7 +64,7 @@ public void accept(Integer v) throws Exception { @Test public void blockingSubscribeConsumerConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -80,7 +80,7 @@ public void accept(Integer v) throws Exception { @Test public void blockingSubscribeConsumerConsumerError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); TestException ex = new TestException(); @@ -100,7 +100,7 @@ public void accept(Object v) throws Exception { @Test public void blockingSubscribeConsumerConsumerAction() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Consumer cons = new Consumer() { @Override @@ -123,7 +123,7 @@ public void run() throws Exception { @Test public void blockingSubscribeObserver() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.range(1, 5) .subscribeOn(Schedulers.computation()) @@ -156,7 +156,7 @@ public void onComplete() { @Test public void blockingSubscribeObserverError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); final TestException ex = new TestException(); @@ -232,7 +232,7 @@ public void utilityClass() { @Test public void disposeUpFront() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); to.dispose(); Observable.just(1).blockingSubscribe(to); @@ -242,7 +242,7 @@ public void disposeUpFront() { @SuppressWarnings("rawtypes") @Test public void delayed() throws Exception { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final Observer[] s = { null }; Schedulers.single().scheduleDirect(new Runnable() { @@ -257,7 +257,7 @@ public void run() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); s[0] = observer; } }.blockingSubscribe(to); @@ -271,14 +271,14 @@ protected void subscribeActual(Observer observer) { @Test public void interrupt() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Thread.currentThread().interrupt(); Observable.never().blockingSubscribe(to); } @Test public void onCompleteDelayed() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.empty().delay(100, TimeUnit.MILLISECONDS) .blockingSubscribe(to); @@ -288,13 +288,13 @@ public void onCompleteDelayed() { @Test public void blockingCancelUpfront() { - BlockingFirstObserver o = new BlockingFirstObserver(); + BlockingFirstObserver o = new BlockingFirstObserver<>(); assertFalse(o.isDisposed()); o.dispose(); assertTrue(o.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); o.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java index 03c437dad9..7085720f01 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.TestHelper; public class ObservableBufferTest extends RxJavaTest { @@ -70,7 +70,7 @@ public void skipAndCountOverlappingBuffers() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("one"); observer.onNext("two"); observer.onNext("three"); @@ -126,7 +126,7 @@ public void timedAndCount() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, "one", 10); push(observer, "two", 90); push(observer, "three", 110); @@ -158,7 +158,7 @@ public void timed() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, "one", 97); push(observer, "two", 98); /** @@ -192,7 +192,7 @@ public void observableBasedOpenerAndCloser() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, "one", 10); push(observer, "two", 60); push(observer, "three", 110); @@ -205,7 +205,7 @@ public void subscribe(Observer observer) { Observable openings = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, new Object(), 50); push(observer, new Object(), 200); complete(observer, 250); @@ -218,7 +218,7 @@ public Observable apply(Object opening) { return Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, new Object(), 100); complete(observer, 101); } @@ -273,7 +273,7 @@ public void accept(List t1) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -303,7 +303,7 @@ public void bufferStopsWhenUnsubscribed1() { Observable source = Observable.never(); Observer> o = TestHelper.mockObserver(); - TestObserver> to = new TestObserver>(o); + TestObserver> to = new TestObserver<>(o); source.buffer(100, 200, TimeUnit.MILLISECONDS, scheduler) .doOnNext(new Consumer>() { @@ -764,7 +764,6 @@ public void onComplete() { assertFalse(observer.isDisposed()); } - @SuppressWarnings("unchecked") @Test public void bufferTimeSkipDefault() { Observable.range(1, 5).buffer(1, 1, TimeUnit.MINUTES) @@ -772,7 +771,6 @@ public void bufferTimeSkipDefault() { .assertResult(Arrays.asList(1, 2, 3, 4, 5)); } - @SuppressWarnings("unchecked") @Test public void bufferBoundaryHint() { Observable.range(1, 5).buffer(Observable.timer(1, TimeUnit.MINUTES), 2) @@ -781,31 +779,29 @@ public void bufferBoundaryHint() { } static HashSet set(Integer... values) { - return new HashSet(Arrays.asList(values)); + return new HashSet<>(Arrays.asList(values)); } - @SuppressWarnings("unchecked") @Test public void bufferIntoCustomCollection() { Observable.just(1, 1, 2, 2, 3, 3, 4, 4) .buffer(3, new Supplier>() { @Override public Collection get() throws Exception { - return new HashSet(); + return new HashSet<>(); } }) .test() .assertResult(set(1, 2), set(2, 3), set(4)); } - @SuppressWarnings("unchecked") @Test public void bufferSkipIntoCustomCollection() { Observable.just(1, 1, 2, 2, 3, 3, 4, 4) .buffer(3, 3, new Supplier>() { @Override public Collection get() throws Exception { - return new HashSet(); + return new HashSet<>(); } }) .test() @@ -813,7 +809,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows() { Observable.just(1) .buffer(1, TimeUnit.SECONDS, Schedulers.single(), Integer.MAX_VALUE, new Supplier>() { @@ -827,7 +822,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows2() { Observable.just(1) .buffer(1, TimeUnit.SECONDS, Schedulers.single(), 10, new Supplier>() { @@ -841,7 +835,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows3() { Observable.just(1) .buffer(2, 1, TimeUnit.SECONDS, Schedulers.single(), new Supplier>() { @@ -855,7 +848,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows4() { Observable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), Integer.MAX_VALUE, new Supplier>() { @@ -865,7 +857,7 @@ public Collection get() throws Exception { if (count++ == 1) { throw new TestException(); } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -875,7 +867,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows5() { Observable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), 10, new Supplier>() { @@ -885,7 +876,7 @@ public Collection get() throws Exception { if (count++ == 1) { throw new TestException(); } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -895,7 +886,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierThrows6() { Observable.never() .buffer(2, 1, TimeUnit.MILLISECONDS, Schedulers.single(), new Supplier>() { @@ -905,7 +895,7 @@ public Collection get() throws Exception { if (count++ == 1) { throw new TestException(); } else { - return new ArrayList(); + return new ArrayList<>(); } } }) @@ -915,7 +905,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierReturnsNull() { Observable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), Integer.MAX_VALUE, new Supplier>() { @@ -925,7 +914,7 @@ public Collection get() throws Exception { if (count++ == 1) { return null; } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -935,7 +924,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierReturnsNull2() { Observable.never() .buffer(1, TimeUnit.MILLISECONDS, Schedulers.single(), 10, new Supplier>() { @@ -945,7 +933,7 @@ public Collection get() throws Exception { if (count++ == 1) { return null; } else { - return new ArrayList(); + return new ArrayList<>(); } } }, false) @@ -955,7 +943,6 @@ public Collection get() throws Exception { } @Test - @SuppressWarnings("unchecked") public void supplierReturnsNull3() { Observable.never() .buffer(2, 1, TimeUnit.MILLISECONDS, Schedulers.single(), new Supplier>() { @@ -965,7 +952,7 @@ public Collection get() throws Exception { if (count++ == 1) { return null; } else { - return new ArrayList(); + return new ArrayList<>(); } } }) @@ -996,7 +983,6 @@ public void dispose() { TestHelper.checkDisposed(PublishSubject.create().buffer(Observable.never(), Functions.justFunction(Observable.never()))); } - @SuppressWarnings("unchecked") @Test public void restartTimer() { Observable.range(1, 5) @@ -1005,7 +991,6 @@ public void restartTimer() { .assertResult(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5)); } - @SuppressWarnings("unchecked") @Test public void bufferSupplierCrash2() { Observable.range(1, 2) @@ -1016,14 +1001,13 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }) .test() .assertFailure(TestException.class, Arrays.asList(1)); } - @SuppressWarnings("unchecked") @Test public void bufferSkipSupplierCrash2() { Observable.range(1, 2) @@ -1034,14 +1018,13 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }) .test() .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferSkipError() { Observable.error(new TestException()) @@ -1050,7 +1033,6 @@ public void bufferSkipError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferSkipOverlap() { Observable.range(1, 5) @@ -1065,7 +1047,6 @@ public void bufferSkipOverlap() { ); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactError() { Observable.error(new TestException()) @@ -1074,7 +1055,6 @@ public void bufferTimedExactError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferTimedSkipError() { Observable.error(new TestException()) @@ -1083,7 +1063,6 @@ public void bufferTimedSkipError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferTimedOverlapError() { Observable.error(new TestException()) @@ -1092,7 +1071,6 @@ public void bufferTimedOverlapError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactEmpty() { Observable.empty() @@ -1101,7 +1079,6 @@ public void bufferTimedExactEmpty() { .assertResult(Collections.emptyList()); } - @SuppressWarnings("unchecked") @Test public void bufferTimedSkipEmpty() { Observable.empty() @@ -1110,7 +1087,6 @@ public void bufferTimedSkipEmpty() { .assertResult(Collections.emptyList()); } - @SuppressWarnings("unchecked") @Test public void bufferTimedOverlapEmpty() { Observable.empty() @@ -1119,7 +1095,6 @@ public void bufferTimedOverlapEmpty() { .assertResult(Collections.emptyList()); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactSupplierCrash() { TestScheduler scheduler = new TestScheduler(); @@ -1134,7 +1109,7 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }, true) .test(); @@ -1149,7 +1124,6 @@ public List get() throws Exception { .assertFailure(TestException.class, Arrays.asList(1)); } - @SuppressWarnings("unchecked") @Test public void bufferTimedExactBoundedError() { TestScheduler scheduler = new TestScheduler(); @@ -1207,7 +1181,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void noCompletionCancelExact() { final AtomicInteger counter = new AtomicInteger(); @@ -1227,7 +1200,6 @@ public void run() throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noCompletionCancelSkip() { final AtomicInteger counter = new AtomicInteger(); @@ -1247,7 +1219,6 @@ public void run() throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noCompletionCancelOverlap() { final AtomicInteger counter = new AtomicInteger(); @@ -1268,7 +1239,6 @@ public void run() throws Exception { } @Test - @SuppressWarnings("unchecked") public void boundaryOpenCloseDisposedOnComplete() { PublishSubject source = PublishSubject.create(); @@ -1308,9 +1278,9 @@ public Integer apply(Integer integer, Long aLong) { } }) .buffer(Observable.interval(0, 200, TimeUnit.MILLISECONDS), - new Function>() { + new Function>() { @Override - public Observable apply(Long a) { + public Observable apply(Long a) { return Observable.just(a).delay(100, TimeUnit.MILLISECONDS); } }) @@ -1331,9 +1301,9 @@ public Integer apply(Integer integer, Long aLong) { } }) .buffer(Observable.interval(0, 100, TimeUnit.MILLISECONDS), - new Function>() { + new Function>() { @Override - public Observable apply(Long a) { + public Observable apply(Long a) { return Observable.just(a).delay(200, TimeUnit.MILLISECONDS); } }) @@ -1344,7 +1314,6 @@ public Observable apply(Long a) { } @Test - @SuppressWarnings("unchecked") public void openClosemainError() { Observable.error(new TestException()) .buffer(Observable.never(), Functions.justFunction(Observable.never())) @@ -1353,15 +1322,14 @@ public void openClosemainError() { } @Test - @SuppressWarnings("unchecked") public void openClosebadSource() { List errors = TestHelper.trackPluginErrors(); try { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); - Disposable bs2 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs1); @@ -1391,7 +1359,6 @@ protected void subscribeActual(Observer observer) { } @Test - @SuppressWarnings("unchecked") public void openCloseOpenCompletes() { PublishSubject source = PublishSubject.create(); @@ -1420,7 +1387,6 @@ public void openCloseOpenCompletes() { } @Test - @SuppressWarnings("unchecked") public void openCloseOpenCompletesNoBuffers() { PublishSubject source = PublishSubject.create(); @@ -1449,7 +1415,6 @@ public void openCloseOpenCompletesNoBuffers() { } @Test - @SuppressWarnings("unchecked") public void openCloseTake() { PublishSubject source = PublishSubject.create(); @@ -1473,7 +1438,6 @@ public void openCloseTake() { } @Test - @SuppressWarnings("unchecked") public void openCloseBadOpen() { List errors = TestHelper.trackPluginErrors(); try { @@ -1484,8 +1448,8 @@ protected void subscribeActual(Observer observer) { assertFalse(((Disposable)observer).isDisposed()); - Disposable bs1 = Disposables.empty(); - Disposable bs2 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs1); @@ -1517,7 +1481,6 @@ protected void subscribeActual(Observer observer) { } @Test - @SuppressWarnings("unchecked") public void openCloseBadClose() { List errors = TestHelper.trackPluginErrors(); try { @@ -1529,8 +1492,8 @@ protected void subscribeActual(Observer observer) { assertFalse(((Disposable)observer).isDisposed()); - Disposable bs1 = Disposables.empty(); - Disposable bs2 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs1); @@ -1574,7 +1537,6 @@ public ObservableSource> apply(Observable f) ); } - @SuppressWarnings("unchecked") @Test public void bufferExactBoundarySecondBufferCrash() { PublishSubject ps = PublishSubject.create(); @@ -1587,7 +1549,7 @@ public List get() throws Exception { if (++calls == 2) { throw new TestException(); } - return new ArrayList(); + return new ArrayList<>(); } }).test(); @@ -1596,24 +1558,23 @@ public List get() throws Exception { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void bufferExactBoundaryBadSource() { Observable ps = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onNext(1); observer.onComplete(); } }; - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); Observable b = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }; @@ -1653,12 +1614,12 @@ public void timedCancelledUpfront() { public void timedInternalState() { TestScheduler sch = new TestScheduler(); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); - BufferExactUnboundedObserver> sub = new BufferExactUnboundedObserver>( - to, Functions.justSupplier((List)new ArrayList()), 1, TimeUnit.SECONDS, sch); + BufferExactUnboundedObserver> sub = new BufferExactUnboundedObserver<>( + to, Functions.justSupplier((List) new ArrayList()), 1, TimeUnit.SECONDS, sch); - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); assertFalse(sub.isDisposed()); @@ -1672,7 +1633,7 @@ public void timedInternalState() { assertTrue(sub.isDisposed()); - sub.buffer = new ArrayList(); + sub.buffer = new ArrayList<>(); sub.enter(); sub.onComplete(); } @@ -1703,12 +1664,12 @@ public Observable> apply(Observable f) public void timedSkipInternalState() { TestScheduler sch = new TestScheduler(); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); - BufferSkipBoundedObserver> sub = new BufferSkipBoundedObserver>( - to, Functions.justSupplier((List)new ArrayList()), 1, 1, TimeUnit.SECONDS, sch.createWorker()); + BufferSkipBoundedObserver> sub = new BufferSkipBoundedObserver<>( + to, Functions.justSupplier((List) new ArrayList()), 1, 1, TimeUnit.SECONDS, sch.createWorker()); - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.enter(); sub.onComplete(); @@ -1722,21 +1683,22 @@ public void timedSkipInternalState() { public void timedSkipCancelWhenSecondBuffer() { TestScheduler sch = new TestScheduler(); - final TestObserver> to = new TestObserver>(); + final TestObserver> to = new TestObserver<>(); - BufferSkipBoundedObserver> sub = new BufferSkipBoundedObserver>( + BufferSkipBoundedObserver> sub = new BufferSkipBoundedObserver<>( to, new Supplier>() { - int calls; - @Override - public List get() throws Exception { - if (++calls == 2) { - to.dispose(); - } - return new ArrayList(); - } - }, 1, 1, TimeUnit.SECONDS, sch.createWorker()); + int calls; + + @Override + public List get() throws Exception { + if (++calls == 2) { + to.dispose(); + } + return new ArrayList<>(); + } + }, 1, 1, TimeUnit.SECONDS, sch.createWorker()); - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.run(); @@ -1747,15 +1709,15 @@ public List get() throws Exception { public void timedSizeBufferAlreadyCleared() { TestScheduler sch = new TestScheduler(); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); BufferExactBoundedObserver> sub = - new BufferExactBoundedObserver>( - to, Functions.justSupplier((List)new ArrayList()), + new BufferExactBoundedObserver<>( + to, Functions.justSupplier((List) new ArrayList()), 1, TimeUnit.SECONDS, 1, false, sch.createWorker()) ; - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); sub.onSubscribe(bs); @@ -1790,10 +1752,10 @@ public ObservableSource> apply(Observable o) @Test public void bufferExactState() { - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); - BufferExactObserver> sub = new BufferExactObserver>( - to, 1, Functions.justSupplier((List)new ArrayList()) + BufferExactObserver> sub = new BufferExactObserver<>( + to, 1, Functions.justSupplier((List) new ArrayList()) ); sub.onComplete(); @@ -1813,7 +1775,6 @@ public ObservableSource> apply(Observable o) } @Test - @SuppressWarnings("unchecked") public void bufferExactFailingSupplier() { Observable.empty() .buffer(1, TimeUnit.SECONDS, Schedulers.computation(), 10, new Supplier>() { @@ -1827,4 +1788,31 @@ public List get() throws Exception { .assertFailure(TestException.class) ; } + + @Test + public void timedUnboundedCancelUpfront() { + Observable.never() + .buffer(1, TimeUnit.SECONDS) + .test(true) + .assertEmpty(); + } + + @Test + public void boundaryCloseCompleteRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + BehaviorSubject bs = BehaviorSubject.createDefault(1); + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = bs + .buffer(BehaviorSubject.createDefault(0), v -> ps) + .test(); + + TestHelper.race( + () -> bs.onComplete(), + () -> ps.onComplete() + ); + + to.assertResult(Arrays.asList(1)); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferUntilSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferUntilSubscriberTest.java index 6d49272de2..eb67fee441 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferUntilSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableBufferUntilSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java index c3695b24d6..74d17c062b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCacheTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,16 +16,21 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import io.reactivex.rxjava3.observables.ConnectableObservable; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.TestObserver; @@ -36,11 +41,11 @@ public class ObservableCacheTest extends RxJavaTest { @Test public void coldReplayNoBackpressure() { - ObservableCache source = new ObservableCache(Observable.range(0, 1000), 16); + ObservableCache source = new ObservableCache<>(Observable.range(0, 1000), 16); assertFalse("Source is connected!", source.isConnected()); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.subscribe(to); @@ -64,7 +69,7 @@ public void cache() throws InterruptedException { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override @@ -119,9 +124,9 @@ public void unsubscribeSource() throws Throwable { @Test public void take() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - ObservableCache cached = new ObservableCache(Observable.range(1, 1000), 16); + ObservableCache cached = new ObservableCache<>(Observable.range(1, 1000), 16); cached.take(10).subscribe(to); to.assertNoErrors(); @@ -135,9 +140,9 @@ public void take() { public void async() { Observable source = Observable.range(1, 10000); for (int i = 0; i < 100; i++) { - TestObserver to1 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); - ObservableCache cached = new ObservableCache(source, 16); + ObservableCache cached = new ObservableCache<>(source, 16); cached.observeOn(Schedulers.computation()).subscribe(to1); @@ -146,7 +151,7 @@ public void async() { to1.assertComplete(); assertEquals(10000, to1.values().size()); - TestObserver to2 = new TestObserver(); + TestObserver to2 = new TestObserver<>(); cached.observeOn(Schedulers.computation()).subscribe(to2); to2.awaitDone(2, TimeUnit.SECONDS); @@ -161,18 +166,18 @@ public void asyncComeAndGo() { Observable source = Observable.interval(1, 1, TimeUnit.MILLISECONDS) .take(1000) .subscribeOn(Schedulers.io()); - ObservableCache cached = new ObservableCache(source, 16); + ObservableCache cached = new ObservableCache<>(source, 16); Observable output = cached.observeOn(Schedulers.computation()); - List> list = new ArrayList>(100); + List> list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); list.add(to); output.skip(i * 10).take(10).subscribe(to); } - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 10; i++) { expected.add((long)(i - 10)); } @@ -198,7 +203,7 @@ public void noMissingBackpressureException() { Observable firehose = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer t) { - t.onSubscribe(Disposables.empty()); + t.onSubscribe(Disposable.empty()); for (int i = 0; i < m; i++) { t.onNext(i); } @@ -206,7 +211,7 @@ public void subscribe(Observer t) { } }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); firehose.cache().observeOn(Schedulers.computation()).takeLast(100).subscribe(to); to.awaitDone(3, TimeUnit.SECONDS); @@ -222,14 +227,14 @@ public void valuesAndThenError() { .concatWith(Observable.error(new TestException())) .cache(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.subscribe(to); to.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); to.assertNotComplete(); to.assertError(TestException.class); - TestObserver to2 = new TestObserver(); + TestObserver to2 = new TestObserver<>(); source.subscribe(to2); to2.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -296,7 +301,7 @@ public void subscribeEmitRace() { cache.test(); - final TestObserverEx to = new TestObserverEx(); + final TestObserverEx to = new TestObserverEx<>(); Runnable r1 = new Runnable() { @Override @@ -341,4 +346,66 @@ public Object call() throws Exception { assertEquals(1, call.get()); } + + @Test + public void addRemoveRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Observable o = Observable.never().cache(); + + TestObserver to = o.test(); + + TestHelper.race( + () -> to.dispose(), + () -> o.test() + ); + } + } + + @Test + public void valuesAreReclaimable() throws Exception { + ConnectableObservable source = + Observable.range(0, 200) + .map($ -> new byte[1024 * 1024]) + .publish(); + + System.out.println("Bounded Replay Leak check: Wait before GC"); + Thread.sleep(1000); + + System.out.println("Bounded Replay Leak check: GC"); + System.gc(); + + Thread.sleep(500); + + final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage memHeap = memoryMXBean.getHeapMemoryUsage(); + long initial = memHeap.getUsed(); + + System.out.printf("Bounded Replay Leak check: Starting: %.3f MB%n", initial / 1024.0 / 1024.0); + + final AtomicLong after = new AtomicLong(); + + source.cache().lastElement().subscribe(new Consumer() { + @Override + public void accept(byte[] v) throws Exception { + System.out.println("Bounded Replay Leak check: Wait before GC 2"); + Thread.sleep(1000); + + System.out.println("Bounded Replay Leak check: GC 2"); + System.gc(); + + Thread.sleep(500); + + after.set(memoryMXBean.getHeapMemoryUsage().getUsed()); + } + }); + + source.connect(); + + System.out.printf("Bounded Replay Leak check: After: %.3f MB%n", after.get() / 1024.0 / 1024.0); + + if (initial + 100 * 1024 * 1024 < after.get()) { + fail("Bounded Replay Leak check: Memory leak detected: " + (initial / 1024.0 / 1024.0) + + " -> " + after.get() / 1024.0 / 1024.0); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCastTest.java index 52576fb9b9..83fdb4b1e5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectTest.java index e31a2299be..92867c2d79 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCollectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,7 +36,7 @@ public void collectToListObservable() { .collect(new Supplier>() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -85,7 +85,7 @@ public void accept(StringBuilder sb, Integer v) { @Test public void collectorFailureDoesNotResultInTwoErrorEmissionsObservable() { try { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); RxJavaPlugins.setErrorHandler(addToList(list)); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); @@ -142,18 +142,17 @@ public void accept(Object o, Integer t) { assertFalse(added.get()); } - @SuppressWarnings("unchecked") @Test public void collectIntoObservable() { Observable.just(1, 1, 1, 1, 2) - .collectInto(new HashSet(), new BiConsumer, Integer>() { + .collectInto(new HashSet<>(), new BiConsumer, Integer>() { @Override public void accept(HashSet s, Integer v) throws Exception { s.add(v); } }).toObservable() .test() - .assertResult(new HashSet(Arrays.asList(1, 2))); + .assertResult(new HashSet<>(Arrays.asList(1, 2))); } @Test @@ -162,7 +161,7 @@ public void collectToList() { .collect(new Supplier>() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -211,7 +210,7 @@ public void accept(StringBuilder sb, Integer v) { @Test public void collectorFailureDoesNotResultInTwoErrorEmissions() { try { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); RxJavaPlugins.setErrorHandler(addToList(list)); final RuntimeException e1 = new RuntimeException(); final RuntimeException e2 = new RuntimeException(); @@ -266,18 +265,17 @@ public void accept(Object o, Integer t) { assertFalse(added.get()); } - @SuppressWarnings("unchecked") @Test public void collectInto() { Observable.just(1, 1, 1, 1, 2) - .collectInto(new HashSet(), new BiConsumer, Integer>() { + .collectInto(new HashSet<>(), new BiConsumer, Integer>() { @Override public void accept(HashSet s, Integer v) throws Exception { s.add(v); } }) .test() - .assertResult(new HashSet(Arrays.asList(1, 2))); + .assertResult(new HashSet<>(Arrays.asList(1, 2))); } @Test @@ -285,7 +283,7 @@ public void dispose() { TestHelper.checkDisposed(Observable.range(1, 3).collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -297,7 +295,7 @@ public void accept(List a, Integer b) throws Exception { TestHelper.checkDisposed(Observable.range(1, 3).collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -315,7 +313,7 @@ public SingleSource> apply(Observable o) throws Exception return o.collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -332,7 +330,7 @@ public ObservableSource> apply(Observable o) throws Excep return o.collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -352,7 +350,7 @@ public Object apply(Observable o) throws Exception { return o.collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java index 4dca384928..2b966ec29c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,7 +19,7 @@ import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; import org.mockito.*; @@ -436,8 +436,8 @@ public List apply(Object[] args) { }; for (int i = 1; i <= n; i++) { System.out.println("test1ToNSources: " + i + " sources"); - List> sources = new ArrayList>(); - List values = new ArrayList(); + List> sources = new ArrayList<>(); + List values = new ArrayList<>(); for (int j = 0; j < i; j++) { sources.add(Observable.just(j)); values.add(j); @@ -467,8 +467,8 @@ public List apply(Object[] args) { }; for (int i = 1; i <= n; i++) { System.out.println("test1ToNSourcesScheduled: " + i + " sources"); - List> sources = new ArrayList>(); - List values = new ArrayList(); + List> sources = new ArrayList<>(); + List values = new ArrayList<>(); for (int j = 0; j < i; j++) { sources.add(Observable.just(j).subscribeOn(Schedulers.io())); values.add(j); @@ -753,7 +753,7 @@ public void accept(Notification n) { } }).take(SIZE); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.combineLatest(timer, Observable. never(), new BiFunction() { @Override @@ -789,7 +789,7 @@ public Object apply(Object[] a) throws Exception { @SuppressWarnings("unchecked") public void combineLatestDelayErrorArrayOfSources() { - Observable.combineLatestDelayError(new ObservableSource[] { + Observable.combineLatestArrayDelayError(new ObservableSource[] { Observable.just(1), Observable.just(2) }, new Function() { @Override @@ -805,7 +805,7 @@ public Object apply(Object[] a) throws Exception { @SuppressWarnings("unchecked") public void combineLatestDelayErrorArrayOfSourcesWithError() { - Observable.combineLatestDelayError(new ObservableSource[] { + Observable.combineLatestArrayDelayError(new ObservableSource[] { Observable.just(1), Observable.just(2).concatWith(Observable.error(new TestException())) }, new Function() { @Override @@ -818,7 +818,6 @@ public Object apply(Object[] a) throws Exception { } @Test - @SuppressWarnings("unchecked") public void combineLatestDelayErrorIterableOfSources() { Observable.combineLatestDelayError(Arrays.asList( @@ -834,7 +833,6 @@ public Object apply(Object[] a) throws Exception { } @Test - @SuppressWarnings("unchecked") public void combineLatestDelayErrorIterableOfSourcesWithError() { Observable.combineLatestDelayError(Arrays.asList( @@ -858,7 +856,7 @@ public void combineLatestArrayEmpty() { @SuppressWarnings("unchecked") @Test public void combineLatestDelayErrorEmpty() { - assertSame(Observable.empty(), Observable.combineLatestDelayError(new ObservableSource[0], Functions.identity(), 16)); + assertSame(Observable.empty(), Observable.combineLatestArrayDelayError(new ObservableSource[0], Functions.identity(), 16)); } @Test @@ -873,7 +871,7 @@ public Object apply(Object a, Object b) throws Exception { @Test public void cancelWhileSubscribing() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.combineLatest( Observable.just(1) @@ -925,7 +923,7 @@ public Object apply(Object a, Object b) throws Exception { @SuppressWarnings("unchecked") @Test public void errorDelayed() { - Observable.combineLatestDelayError( + Observable.combineLatestArrayDelayError( new ObservableSource[] { Observable.error(new TestException()), Observable.just(1) }, new Function() { @Override @@ -942,7 +940,7 @@ public Object apply(Object[] a) throws Exception { @SuppressWarnings("unchecked") @Test public void errorDelayed2() { - Observable.combineLatestDelayError( + Observable.combineLatestArrayDelayError( new ObservableSource[] { Observable.error(new TestException()).startWithItem(1), Observable.empty() }, new Function() { @Override @@ -1044,7 +1042,6 @@ public Object apply(Object a, Object b) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void dontSubscribeIfDone2() { List errors = TestHelper.trackPluginErrors(); @@ -1078,7 +1075,6 @@ public Object apply(Object[] a) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void combine2Observable2Errors() throws Exception { List errors = TestHelper.trackPluginErrors(); @@ -1195,7 +1191,6 @@ public Integer apply(Integer t1, Integer t2) throws Exception { } @Test - @SuppressWarnings("unchecked") public void syncFirstErrorsAfterItemDelayError() { Observable.combineLatestDelayError(Arrays.asList( Observable.just(21).concatWith(Observable.error(new TestException())), @@ -1213,7 +1208,6 @@ public Object apply(Object[] a) throws Exception { .assertFailure(TestException.class, 42); } - @SuppressWarnings("unchecked") @Test public void observableSourcesInIterable() { ObservableSource source = new ObservableSource() { @@ -1232,4 +1226,76 @@ public Integer apply(Object[] t) throws Throwable { .test() .assertResult(2); } + + @Test + public void onCompleteDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestObserver to = new TestObserver<>(); + PublishSubject ps = PublishSubject.create(); + + Observable.combineLatest(ps, Observable.never(), (a, b) -> a) + .subscribe(to); + + TestHelper.race(() -> ps.onComplete(), () -> to.dispose()); + } + } + + @Test + public void onErrorDisposeDelayErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex = new TestException(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + TestObserverEx to = new TestObserverEx<>(); + AtomicReference> ref = new AtomicReference<>(); + Observable o = new Observable() { + @Override + public void subscribeActual(Observer observer) { + ref.set(observer); + } + }; + + Observable.combineLatestDelayError(Arrays.asList(o, Observable.never()), (a) -> a) + .subscribe(to); + + ref.get().onSubscribe(Disposable.empty()); + + TestHelper.race(() -> ref.get().onError(ex), () -> to.dispose()); + + if (to.errors().isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + } + }); + } + + @Test + public void doneButNotEmpty() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = Observable.combineLatest(ps1, ps2, (a, b) -> a + b) + .doOnNext(v -> { + if (v == 2) { + ps2.onNext(3); + ps2.onComplete(); + ps1.onComplete(); + } + }) + .test(); + + ps1.onNext(1); + ps2.onNext(1); + + to.assertResult(2, 4); + } + + @Test + public void iterableNullPublisher() { + Observable.combineLatest(Arrays.asList(Observable.never(), null), (a) -> a) + .test() + .assertFailure(NullPointerException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapCompletableTest.java index a3cd0dcdb5..931fcd403f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapCompletableTest.java @@ -1,15 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.observable; import static org.junit.Assert.assertTrue; @@ -85,7 +86,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); @@ -248,4 +249,4 @@ public CompletableSource apply(Integer v) throws Exception { } }; } -} \ No newline at end of file +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java index 4e9a2b0d1f..1e75ff9aef 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapEagerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -197,7 +197,7 @@ public Observable apply(Integer t) { @Before public void before() { - to = new TestObserver(); + to = new TestObserver<>(); } @Test @@ -218,7 +218,6 @@ public void simple2() { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness2() { final AtomicInteger count = new AtomicInteger(); @@ -238,7 +237,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness3() { final AtomicInteger count = new AtomicInteger(); @@ -258,7 +256,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness4() { final AtomicInteger count = new AtomicInteger(); @@ -278,7 +275,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness5() { final AtomicInteger count = new AtomicInteger(); @@ -298,7 +294,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness6() { final AtomicInteger count = new AtomicInteger(); @@ -318,7 +313,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness7() { final AtomicInteger count = new AtomicInteger(); @@ -338,7 +332,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness8() { final AtomicInteger count = new AtomicInteger(); @@ -358,7 +351,6 @@ public void accept(Integer t) { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void eagerness9() { final AtomicInteger count = new AtomicInteger(); @@ -387,7 +379,6 @@ public void mainError() { to.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void innerError() { // TODO verify: concatMapEager subscribes first then consumes the sources is okay @@ -404,7 +395,6 @@ public void innerError() { to.assertNotComplete(); } - @SuppressWarnings("unchecked") @Test public void innerEmpty() { Observable.concatArrayEager(Observable.empty(), Observable.empty()).subscribe(to); @@ -503,7 +493,6 @@ public void concatArrayEager() throws Exception { } } - @SuppressWarnings("unchecked") @Test public void capacityHint() { Observable source = Observable.just(1); @@ -540,14 +529,13 @@ public void ObservableCapacityHint() { to.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void badCapacityHint() throws Exception { Observable source = Observable.just(1); try { Observable.concatEager(Arrays.asList(source, source, source), 1, -99); } catch (IllegalArgumentException ex) { - assertEquals("prefetch > 0 required but it was -99", ex.getMessage()); + assertEquals("bufferSize > 0 required but it was -99", ex.getMessage()); } } @@ -559,12 +547,11 @@ public void mappingBadCapacityHint() throws Exception { try { Observable.just(source, source, source).concatMapEager((Function)Functions.identity(), 10, -99); } catch (IllegalArgumentException ex) { - assertEquals("prefetch > 0 required but it was -99", ex.getMessage()); + assertEquals("bufferSize > 0 required but it was -99", ex.getMessage()); } } - @SuppressWarnings("unchecked") @Test public void concatEagerIterable() { Observable.concatEager(Arrays.asList(Observable.just(1), Observable.just(2))) @@ -723,7 +710,7 @@ public void run() { @Test public void mapperCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1).hide() .concatMapEager(new Function>() { @@ -837,7 +824,7 @@ public ObservableSource apply(Integer i) throws Exception { public void maxConcurrencyOf2() { List[] list = new ArrayList[100]; for (int i = 0; i < 100; i++) { - List lst = new ArrayList(); + List lst = new ArrayList<>(); list[i] = lst; for (int k = 1; k <= 10; k++) { lst.add((i) * 10 + k); @@ -873,7 +860,6 @@ public void arrayDelayErrorDefault() { PublishSubject ps2 = PublishSubject.create(); PublishSubject ps3 = PublishSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable.concatArrayEagerDelayError(ps1, ps2, ps3) .test(); @@ -907,7 +893,6 @@ public void arrayDelayErrorMaxConcurrency() { PublishSubject ps2 = PublishSubject.create(); PublishSubject ps3 = PublishSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable.concatArrayEagerDelayError(2, 2, ps1, ps2, ps3) .test(); @@ -943,7 +928,6 @@ public void arrayDelayErrorMaxConcurrencyErrorDelayed() { PublishSubject ps2 = PublishSubject.create(); PublishSubject ps3 = PublishSubject.create(); - @SuppressWarnings("unchecked") TestObserver to = Observable.concatArrayEagerDelayError(2, 2, ps1, ps2, ps3) .test(); @@ -1050,4 +1034,57 @@ public Observable apply(Integer v) throws Throwable { } }); } + + @Test + public void iterableDelayError() { + Observable.concatEagerDelayError(Arrays.asList( + Observable.range(1, 2), + Observable.error(new TestException()), + Observable.range(3, 3) + )) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void iterableDelayErrorMaxConcurrency() { + Observable.concatEagerDelayError(Arrays.asList( + Observable.range(1, 2), + Observable.error(new TestException()), + Observable.range(3, 3) + ), 1, 1) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void observerDelayError() { + Observable.concatEagerDelayError(Observable.fromArray( + Observable.range(1, 2), + Observable.error(new TestException()), + Observable.range(3, 3) + )) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void observerDelayErrorMaxConcurrency() { + Observable.concatEagerDelayError(Observable.fromArray( + Observable.range(1, 2), + Observable.error(new TestException()), + Observable.range(3, 3) + ), 1, 1) + .test() + .assertFailure(TestException.class, 1, 2, 3, 4, 5); + } + + @Test + public void innerFusionRejected() { + Observable.just(1) + .hide() + .concatMapEager(v -> TestHelper.rejectObservableFusion()) + .test() + .assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java index ace3f55493..f2a6ede314 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,12 +22,14 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.observers.*; @@ -305,7 +307,7 @@ public Observable apply(Integer t) throws Throwable { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -350,7 +352,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeout = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeout, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeout + " milliseconds."); + } assertEquals(n, counter.get()); } @@ -367,7 +373,7 @@ public void concatMapRangeAsyncLoopIssue2876() { if (i % 1000 == 0) { System.out.println("concatMapRangeAsyncLoop > " + i); } - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(0, 1000) .concatMap(new Function>() { @Override @@ -663,7 +669,7 @@ public void badInnerSource() { @Override protected void subscribeActual(Observer o) { ts0[0] = o; - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); o.onError(new TestException("First")); } }), 2, ImmediateThinScheduler.INSTANCE) @@ -689,7 +695,7 @@ public void badInnerSourceDelayError() { @Override protected void subscribeActual(Observer o) { ts0[0] = o; - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); o.onError(new TestException("First")); } }), true, 2, ImmediateThinScheduler.INSTANCE) @@ -1049,4 +1055,70 @@ public Observable apply(Integer v) throws Throwable { } }); } + + @Test + public void fusionRejected() { + TestObserverEx to = new TestObserverEx<>(); + + TestHelper.rejectObservableFusion() + .concatMap(v -> Observable.never(), 2, ImmediateThinScheduler.INSTANCE) + .subscribe(to); + } + + @Test + public void fusionRejectedDelayErrorr() { + TestObserverEx to = new TestObserverEx<>(); + + TestHelper.rejectObservableFusion() + .concatMapDelayError(v -> Observable.never(), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(to); + } + + @Test + public void scalarInnerJustDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.fromCallable(() -> { + to.dispose(); + return 1; + }), true, 2, ImmediateThinScheduler.INSTANCE) + .subscribe(to); + + to.assertEmpty(); + } + + static final class EmptyDisposingObservable extends Observable + implements Supplier { + final TestObserver to; + EmptyDisposingObservable(TestObserver to) { + this.to = to; + } + + @Override + protected void subscribeActual(@NonNull Observer observer) { + EmptyDisposable.complete(observer); + } + + @Override + public @NonNull Object get() throws Throwable { + to.dispose(); + return null; + } + } + + @Test + public void scalarInnerEmptyDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> new EmptyDisposingObservable(to), + true, 2, ImmediateThinScheduler.INSTANCE + ) + .subscribe(to); + + to.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java index 61b1f1579b..8fb29f2eb4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,10 +22,11 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.operators.observable.ObservableConcatMapSchedulerTest.EmptyDisposingObservable; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.*; @@ -150,7 +151,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); @@ -181,7 +182,7 @@ public void badSourceDelayError() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); @@ -355,7 +356,7 @@ public ObservableSource apply(Integer v) throws Exception { @Override protected void subscribeActual(Observer observer) { o[0] = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }; @@ -372,7 +373,6 @@ protected void subscribeActual(Observer observer) { } } - @SuppressWarnings("unchecked") @Test public void concatReportsDisposedOnComplete() { final Disposable[] disposable = { null }; @@ -404,7 +404,6 @@ public void onComplete() { } @Test - @SuppressWarnings("unchecked") public void concatReportsDisposedOnError() { final Disposable[] disposable = { null }; @@ -567,4 +566,108 @@ public Observable apply(Integer v) throws Throwable { } }); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.concatMap(v -> Observable.never())); + } + + @Test + public void doubleOnSubscribeDelayError() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.concatMapDelayError(v -> Observable.never())); + } + + @Test + public void scalarXMap() { + Observable.fromCallable(() -> 1) + .concatMap(v -> Observable.just(2).hide()) + .test() + .assertResult(2); + } + + @Test + public void rejectedFusion() { + TestHelper.rejectObservableFusion() + .concatMap(v -> Observable.never()) + .test(); + } + + @Test + public void rejectedFusionDelayError() { + TestHelper.rejectObservableFusion() + .concatMapDelayError(v -> Observable.never()) + .test(); + } + + @Test + public void asyncFusedDelayError() { + UnicastSubject uc = UnicastSubject.create(); + + TestObserver to = uc.concatMapDelayError(v -> Observable.just(v).hide()) + .test(); + + uc.onNext(1); + uc.onComplete(); + + to.assertResult(1); + } + + @Test + public void scalarInnerJustDelayError() { + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.just(v)) + .test() + .assertResult(1); + } + + @Test + public void scalarInnerEmptyDelayError() { + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.empty()) + .test() + .assertResult(); + } + + @Test + public void scalarInnerJustDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> Observable.fromCallable(() -> { + to.dispose(); + return 1; + })) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void scalarInnerEmptyDisposeDelayError() { + TestObserver to = new TestObserver<>(); + + Observable.just(1) + .hide() + .concatMapDelayError(v -> new EmptyDisposingObservable(to)) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void delayErrorInnerActive() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = Observable.range(1, 5) + .hide() + .concatMapDelayError(v -> ps) + .test(); + + ps.onComplete(); + + to.assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java index 0e8c64b92d..d14f803ee9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -63,7 +63,7 @@ public void concatWithList() { final Observable odds = Observable.fromArray(o); final Observable even = Observable.fromArray(e); - final List> list = new ArrayList>(); + final List> list = new ArrayList<>(); list.add(odds); list.add(even); Observable concat = Observable.concat(Observable.fromIterable(list)); @@ -86,7 +86,7 @@ public void concatObservableOfObservables() { @Override public void subscribe(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); // simulate what would happen in an Observable observer.onNext(odds); observer.onNext(even); @@ -108,8 +108,8 @@ public void subscribe(Observer> observer) { public void simpleAsyncConcat() { Observer observer = TestHelper.mockObserver(); - TestObservable o1 = new TestObservable("one", "two", "three"); - TestObservable o2 = new TestObservable("four", "five", "six"); + TestObservable o1 = new TestObservable<>("one", "two", "three"); + TestObservable o2 = new TestObservable<>("four", "five", "six"); Observable.concat(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2)).subscribe(observer); @@ -148,12 +148,12 @@ public void nestedAsyncConcatLoop() throws Throwable { public void nestedAsyncConcat() throws InterruptedException { Observer observer = TestHelper.mockObserver(); - final TestObservable o1 = new TestObservable("one", "two", "three"); - final TestObservable o2 = new TestObservable("four", "five", "six"); - final TestObservable o3 = new TestObservable("seven", "eight", "nine"); + final TestObservable o1 = new TestObservable<>("one", "two", "three"); + final TestObservable o2 = new TestObservable<>("four", "five", "six"); + final TestObservable o3 = new TestObservable<>("seven", "eight", "nine"); final CountDownLatch allowThird = new CountDownLatch(1); - final AtomicReference parent = new AtomicReference(); + final AtomicReference parent = new AtomicReference<>(); final CountDownLatch parentHasStarted = new CountDownLatch(1); final CountDownLatch parentHasFinished = new CountDownLatch(1); @@ -161,7 +161,7 @@ public void nestedAsyncConcat() throws InterruptedException { @Override public void subscribe(final Observer> observer) { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); observer.onSubscribe(d); parent.set(new Thread(new Runnable() { @@ -271,8 +271,8 @@ public void blockedObservableOfObservables() { final Observable even = Observable.fromArray(e); final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(callOnce, okToContinue, odds, even); + + TestObservable> observableOfObservables = new TestObservable<>(callOnce, okToContinue, odds, even); Observable concatF = Observable.concat(Observable.unsafeCreate(observableOfObservables)); concatF.subscribe(observer); try { @@ -304,14 +304,13 @@ public void blockedObservableOfObservables() { @Test public void concatConcurrentWithInfinity() { - final TestObservable w1 = new TestObservable("one", "two", "three"); + final TestObservable w1 = new TestObservable<>("one", "two", "three"); //This Observable will send "hello" MAX_VALUE time. - final TestObservable w2 = new TestObservable("hello", Integer.MAX_VALUE); + final TestObservable w2 = new TestObservable<>("hello", Integer.MAX_VALUE); Observer observer = TestHelper.mockObserver(); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); + TestObservable> observableOfObservables = new TestObservable<>(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); Observable concatF = Observable.concat(Observable.unsafeCreate(observableOfObservables)); concatF.take(50).subscribe(observer); @@ -339,8 +338,8 @@ public void concatNonBlockingObservables() { final CountDownLatch okToContinueW1 = new CountDownLatch(1); final CountDownLatch okToContinueW2 = new CountDownLatch(1); - final TestObservable w1 = new TestObservable(null, okToContinueW1, "one", "two", "three"); - final TestObservable w2 = new TestObservable(null, okToContinueW2, "four", "five", "six"); + final TestObservable w1 = new TestObservable<>(null, okToContinueW1, "one", "two", "three"); + final TestObservable w2 = new TestObservable<>(null, okToContinueW2, "four", "five", "six"); Observer observer = TestHelper.mockObserver(); @@ -348,7 +347,7 @@ public void concatNonBlockingObservables() { @Override public void subscribe(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); // simulate what would happen in an Observable observer.onNext(Observable.unsafeCreate(w1)); observer.onNext(Observable.unsafeCreate(w2)); @@ -390,11 +389,11 @@ public void subscribe(Observer> observer) { public void concatUnsubscribe() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); - final TestObservable w1 = new TestObservable("one", "two", "three"); - final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); + final TestObservable w1 = new TestObservable<>("one", "two", "three"); + final TestObservable w2 = new TestObservable<>(callOnce, okToContinue, "four", "five", "six"); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); final Observable concat = Observable.concat(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); @@ -432,14 +431,13 @@ public void concatUnsubscribe() { public void concatUnsubscribeConcurrent() { final CountDownLatch callOnce = new CountDownLatch(1); final CountDownLatch okToContinue = new CountDownLatch(1); - final TestObservable w1 = new TestObservable("one", "two", "three"); - final TestObservable w2 = new TestObservable(callOnce, okToContinue, "four", "five", "six"); + final TestObservable w1 = new TestObservable<>("one", "two", "three"); + final TestObservable w2 = new TestObservable<>(callOnce, okToContinue, "four", "five", "six"); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); - @SuppressWarnings("unchecked") - TestObservable> observableOfObservables = new TestObservable>(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); + TestObservable> observableOfObservables = new TestObservable<>(Observable.unsafeCreate(w1), Observable.unsafeCreate(w2)); Observable concatF = Observable.concat(Observable.unsafeCreate(observableOfObservables)); concatF.subscribe(to); @@ -493,10 +491,12 @@ public boolean isDisposed() { private final T seed; private final int size; + @SafeVarargs TestObservable(T... values) { this(null, null, values); } + @SafeVarargs TestObservable(CountDownLatch once, CountDownLatch okToContinue, T... values) { this.values = Arrays.asList(values); this.size = this.values.size(); @@ -617,7 +617,7 @@ public Observable apply(Integer v) { result.subscribe(o); - List list = new ArrayList(n); + List list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(i); } @@ -642,7 +642,7 @@ public Observable apply(Integer v) { result.subscribe(o); - List list = new ArrayList(n); + List list = new ArrayList<>(n); for (int i = 0; i < n / 2; i++) { list.add(i); } @@ -666,7 +666,7 @@ public void concatWithNonCompliantSourceDoubleOnComplete() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("hello"); observer.onComplete(); observer.onComplete(); @@ -674,7 +674,7 @@ public void subscribe(Observer observer) { }); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.concat(o, o).subscribe(to); to.awaitDone(500, TimeUnit.MILLISECONDS); to.assertTerminated(); @@ -683,7 +683,7 @@ public void subscribe(Observer observer) { } @Test - public void issue2890NoStackoverflow() throws InterruptedException { + public void issue2890NoStackoverflow() throws InterruptedException, TimeoutException { final ExecutorService executor = Executors.newFixedThreadPool(2); final Scheduler sch = Schedulers.from(executor); @@ -728,7 +728,11 @@ public void onError(Throwable e) { } }); - executor.awaitTermination(20000, TimeUnit.MILLISECONDS); + long awaitTerminationTimeout = 100_000; + if (!executor.awaitTermination(awaitTerminationTimeout, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Completed " + counter.get() + "/" + n + " before timed out after " + + awaitTerminationTimeout + " milliseconds."); + } assertEquals(n, counter.get()); } @@ -745,7 +749,7 @@ public void concatMapRangeAsyncLoopIssue2876() { if (i % 1000 == 0) { System.out.println("concatMapRangeAsyncLoop > " + i); } - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(0, 1000) .concatMap(new Function>() { @@ -780,7 +784,6 @@ public void concat4() { .assertResult(1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatArrayDelayError() { Observable.concatArrayDelayError(Observable.just(1), Observable.just(2), @@ -789,7 +792,6 @@ public void concatArrayDelayError() { .assertResult(1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatArrayDelayErrorWithError() { Observable.concatArrayDelayError(Observable.just(1), Observable.just(2), @@ -799,7 +801,6 @@ public void concatArrayDelayErrorWithError() { .assertFailure(TestException.class, 1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatIterableDelayError() { Observable.concatDelayError( @@ -809,7 +810,6 @@ public void concatIterableDelayError() { .assertResult(1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatIterableDelayErrorWithError() { Observable.concatDelayError( @@ -883,18 +883,16 @@ public void concatMapIterableBufferSize() { public Iterable apply(Integer v) throws Exception { return Arrays.asList(1, 2, 3, 4, 5); } - }, 1) + }) .test() .assertResult(1, 2, 3, 4, 5, 1, 2, 3, 4, 5); } - @SuppressWarnings("unchecked") @Test public void emptyArray() { assertSame(Observable.empty(), Observable.concatArrayDelayError()); } - @SuppressWarnings("unchecked") @Test public void singleElementArray() { assertSame(Observable.never(), Observable.concatArrayDelayError(Observable.never())); @@ -925,13 +923,11 @@ public ObservableSource apply(Object v) throws Exception { } - @SuppressWarnings("unchecked") @Test public void concatArrayEmpty() { assertSame(Observable.empty(), Observable.concatArray()); } - @SuppressWarnings("unchecked") @Test public void concatArraySingleElement() { assertSame(Observable.never(), Observable.concatArray(Observable.never())); @@ -962,7 +958,6 @@ public ObservableSource apply(Object v) throws Exception { } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscription() { final int[] calls = { 0 }; @@ -983,7 +978,6 @@ public void subscribe(ObservableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionDelayError() { final int[] calls = { 0 }; @@ -1004,7 +998,6 @@ public void subscribe(ObservableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionIterable() { final int[] calls = { 0 }; @@ -1025,7 +1018,6 @@ public void subscribe(ObservableEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionDelayErrorIterable() { final int[] calls = { 0 }; @@ -1075,7 +1067,6 @@ public void onComplete() { } @Test - @SuppressWarnings("unchecked") public void concatReportsDisposedOnCompleteDelayError() { final Disposable[] disposable = { null }; @@ -1132,7 +1123,6 @@ public void onComplete() { } @Test - @SuppressWarnings("unchecked") public void concatReportsDisposedOnErrorDelayError() { final Disposable[] disposable = { null }; @@ -1160,7 +1150,6 @@ public void onComplete() { assertTrue(disposable[0].isDisposed()); } - @SuppressWarnings("unchecked") @Test public void noCancelPreviousArray() { final AtomicInteger counter = new AtomicInteger(); @@ -1179,7 +1168,6 @@ public void run() throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noCancelPreviousIterable() { final AtomicInteger counter = new AtomicInteger(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletableTest.java index 65d85f20d5..5e0d878a34 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public class ObservableConcatWithCompletableTest extends RxJavaTest { @Test public void normal() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Completable.fromAction(new Action() { @@ -44,7 +44,7 @@ public void run() throws Exception { @Test public void mainError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.error(new TestException()) .concatWith(Completable.fromAction(new Action() { @@ -60,7 +60,7 @@ public void run() throws Exception { @Test public void otherError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Completable.error(new TestException())) @@ -71,7 +71,7 @@ public void otherError() { @Test public void takeMain() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Completable.fromAction(new Action() { @@ -106,10 +106,10 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); - Disposable bs2 = Disposables.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs2); assertFalse(bs1.isDisposed()); @@ -127,7 +127,7 @@ public void consumerDisposed() { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); assertFalse(((Disposable)observer).isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybeTest.java index 4d4f94954a..6c70e75a9d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public class ObservableConcatWithMaybeTest extends RxJavaTest { @Test public void normalEmpty() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Maybe.fromAction(new Action() { @@ -44,7 +44,7 @@ public void run() throws Exception { @Test public void normalNonEmpty() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Maybe.just(100)) @@ -55,7 +55,7 @@ public void normalNonEmpty() { @Test public void mainError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.error(new TestException()) .concatWith(Maybe.fromAction(new Action() { @@ -71,7 +71,7 @@ public void run() throws Exception { @Test public void otherError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Maybe.error(new TestException())) @@ -82,7 +82,7 @@ public void otherError() { @Test public void takeMain() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Maybe.fromAction(new Action() { @@ -117,7 +117,7 @@ public void consumerDisposed() { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); assertFalse(((Disposable)observer).isDisposed()); @@ -138,10 +138,10 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); - Disposable bs2 = Disposables.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs2); assertFalse(bs1.isDisposed()); @@ -159,10 +159,10 @@ public void badSource2() { Flowable.empty().concatWith(new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); - Disposable bs2 = Disposables.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs2); assertFalse(bs1.isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingleTest.java index 73a0ff3c0d..8ac2519c3c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableConcatWithSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class ObservableConcatWithSingleTest extends RxJavaTest { @Test public void normal() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Single.just(100)) @@ -38,7 +38,7 @@ public void normal() { @Test public void mainError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.error(new TestException()) .concatWith(Single.just(100)) @@ -49,7 +49,7 @@ public void mainError() { @Test public void otherError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Single.error(new TestException())) @@ -60,7 +60,7 @@ public void otherError() { @Test public void takeMain() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5) .concatWith(Single.just(100)) @@ -90,7 +90,7 @@ public void consumerDisposed() { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); assertFalse(((Disposable)observer).isDisposed()); @@ -111,10 +111,10 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); - Disposable bs2 = Disposables.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs2); assertFalse(bs1.isDisposed()); @@ -132,10 +132,10 @@ public void badSource2() { Flowable.empty().concatWith(new Single() { @Override protected void subscribeActual(SingleObserver observer) { - Disposable bs1 = Disposables.empty(); + Disposable bs1 = Disposable.empty(); observer.onSubscribe(bs1); - Disposable bs2 = Disposables.empty(); + Disposable bs2 = Disposable.empty(); observer.onSubscribe(bs2); assertFalse(bs1.isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountTest.java index 4207fee66f..1390502816 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCountTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java index 4a22746c54..0b96fae6aa 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableCreateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,26 +17,25 @@ import java.io.IOException; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Cancellable; +import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.testsupport.*; public class ObservableCreateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void nullArgument() { - Observable.create(null); - } - @Test + @SuppressUndeliverable public void basic() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Observable.create(new ObservableOnSubscribe() { @Override @@ -60,9 +59,10 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicWithCancellable() { - final Disposable d1 = Disposables.empty(); - final Disposable d2 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); + final Disposable d2 = Disposable.empty(); Observable.create(new ObservableOnSubscribe() { @Override @@ -93,8 +93,9 @@ public void cancel() throws Exception { } @Test + @SuppressUndeliverable public void basicWithError() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Observable.create(new ObservableOnSubscribe() { @Override @@ -117,8 +118,9 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicSerialized() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Observable.create(new ObservableOnSubscribe() { @Override @@ -144,8 +146,9 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicWithErrorSerialized() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Observable.create(new ObservableOnSubscribe() { @Override @@ -174,7 +177,7 @@ public void wrap() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onNext(3); @@ -192,7 +195,7 @@ public void unsafe() { Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onNext(3); @@ -211,6 +214,7 @@ public void unsafeWithObservable() { } @Test + @SuppressUndeliverable public void createNullValue() { final Throwable[] error = { null }; @@ -234,6 +238,7 @@ public void subscribe(ObservableEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void createNullValueSerialized() { final Throwable[] error = { null }; @@ -322,7 +327,7 @@ public void onErrorCrash() { Observable.create(new ObservableOnSubscribe() { @Override public void subscribe(ObservableEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { e.onError(new IOException()); @@ -358,7 +363,7 @@ public void onCompleteCrash() { Observable.create(new ObservableOnSubscribe() { @Override public void subscribe(ObservableEmitter e) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); e.setDisposable(d); try { e.onComplete(); @@ -659,4 +664,96 @@ public void subscribe(ObservableEmitter emitter) throws Exception { } }).test().assertEmpty(); } + + @Test + public void emptySerialized() { + Observable.create(emitter -> emitter.serialize().onComplete()) + .test() + .assertResult(); + } + + @Test + public void serializedDisposedBeforeOnNext() { + TestObserver to = new TestObserver<>(); + + Observable.create(emitter -> { + to.dispose(); + emitter.serialize().onNext(1); + }) + .subscribe(to); + + to.assertEmpty(); + } + + @Test + public void serializedOnNextAfterComplete() { + TestObserver to = new TestObserver<>(); + + Observable.create(emitter -> { + emitter = emitter.serialize(); + + emitter.onComplete(); + emitter.onNext(1); + }) + .subscribe(to); + + to.assertResult(); + } + + @Test + public void serializedEnqueueAndDrainRace() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestObserver to = new TestObserver<>(); + AtomicReference> ref = new AtomicReference<>(); + + CountDownLatch cdl = new CountDownLatch(1); + + Observable.create(emitter -> { + emitter = emitter.serialize(); + ref.set(emitter); + emitter.onNext(1); + }) + .doOnNext(v -> { + if (v == 1) { + TestHelper.raceOther(() -> { + ref.get().onNext(2); + }, cdl); + ref.get().onNext(3); + } + }) + .subscribe(to); + + cdl.await(); + + to.assertValueCount(3); + } + } + + @Test + public void serializedDrainDoneButNotEmpty() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestObserver to = new TestObserver<>(); + AtomicReference> ref = new AtomicReference<>(); + + CountDownLatch cdl = new CountDownLatch(1); + + Observable.create(emitter -> { + emitter = emitter.serialize(); + ref.set(emitter); + emitter.onNext(1); + }) + .doOnNext(v -> { + if (v == 1) { + TestHelper.raceOther(() -> { + ref.get().onNext(2); + ref.get().onComplete(); + }, cdl); + ref.get().onNext(3); + } + }) + .subscribe(to); + + cdl.await(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java index d66de923f8..fc4e7d8478 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDebounceTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,6 +21,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.functions.Action; import org.junit.*; import org.mockito.InOrder; import org.reactivestreams.Publisher; @@ -50,12 +51,82 @@ public void before() { innerScheduler = scheduler.createWorker(); } + @Test + public void debounceWithOnDroppedCallbackWithEx() throws Throwable { + Observable source = Observable.unsafeCreate(new ObservableSource() { + @Override + public void subscribe(Observer observer) { + observer.onSubscribe(Disposable.empty()); + publishNext(observer, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. + publishNext(observer, 400, "two"); // Should be published since "three" will arrive after the timeout expires. + publishNext(observer, 900, "three"); // Should be skipped since onComplete will arrive before the timeout expires. + publishNext(observer, 999, "four"); // Should be skipped since onComplete will arrive before the timeout expires. + publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. + } + }); + + Action whenDisposed = mock(Action.class); + Observable sampled = source + .doOnDispose(whenDisposed) + .debounce(400, TimeUnit.MILLISECONDS, scheduler, e -> { + if ("three".equals(e)) { + throw new TestException("forced"); + } + }); + sampled.subscribe(observer); + + scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + // must go to 800 since it must be 400 after when two is sent, which is at 400 + scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("two"); + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onError(any(TestException.class)); + inOrder.verify(observer, never()).onNext("three"); + inOrder.verify(observer, never()).onNext("four"); + inOrder.verify(observer, never()).onComplete(); + inOrder.verifyNoMoreInteractions(); + verify(whenDisposed).run(); + } + + @Test + public void debounceWithOnDroppedCallback() { + Observable source = Observable.unsafeCreate(new ObservableSource() { + @Override + public void subscribe(Observer observer) { + observer.onSubscribe(Disposable.empty()); + publishNext(observer, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. + publishNext(observer, 400, "two"); // Should be published since "three" will arrive after the timeout expires. + publishNext(observer, 900, "three"); // Should be skipped since onComplete will arrive before the timeout expires. + publishNext(observer, 999, "four"); // Should be skipped since onComplete will arrive before the timeout expires. + publishCompleted(observer, 1000); // Should be published as soon as the timeout expires. + } + }); + + Observer drops = TestHelper.mockObserver(); + InOrder inOrderDrops = inOrder(drops); + Observable sampled = source.debounce(400, TimeUnit.MILLISECONDS, scheduler, drops::onNext); + sampled.subscribe(observer); + + scheduler.advanceTimeTo(0, TimeUnit.MILLISECONDS); + InOrder inOrder = inOrder(observer); + // must go to 800 since it must be 400 after when two is sent, which is at 400 + scheduler.advanceTimeTo(800, TimeUnit.MILLISECONDS); + inOrderDrops.verify(drops, times(1)).onNext("one"); + inOrder.verify(observer, times(1)).onNext("two"); + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrderDrops.verify(drops, times(1)).onNext("three"); + inOrder.verify(observer, times(1)).onComplete(); + inOrder.verifyNoMoreInteractions(); + inOrderDrops.verifyNoMoreInteractions(); + } + @Test public void debounceWithCompleted() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); publishNext(observer, 100, "one"); // Should be skipped since "two" will arrive before the timeout expires. publishNext(observer, 400, "two"); // Should be published since "three" will arrive after the timeout expires. publishNext(observer, 900, "three"); // Should be skipped since onComplete will arrive before the timeout expires. @@ -81,7 +152,7 @@ public void debounceNeverEmits() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); // all should be skipped since they are happening faster than the 200ms timeout publishNext(observer, 100, "a"); // Should be skipped publishNext(observer, 200, "b"); // Should be skipped @@ -111,7 +182,7 @@ public void debounceWithError() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); Exception error = new TestException(); publishNext(observer, 100, "one"); // Should be published since "two" will arrive after the timeout expires. publishNext(observer, 600, "two"); // Should be skipped since onError will arrive before the timeout expires. @@ -288,7 +359,7 @@ public Observable apply(Integer t1) { @Test public void debounceWithTimeBackpressure() throws InterruptedException { TestScheduler scheduler = new TestScheduler(); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); Observable.merge( Observable.just(1), @@ -317,7 +388,7 @@ public void dispose() { TestHelper.checkDisposed(PublishSubject.create().debounce(Functions.justFunction(Observable.never()))); - Disposable d = new ObservableDebounceTimed.DebounceEmitter(1, 1, null); + Disposable d = new ObservableDebounceTimed.DebounceEmitter<>(1, 1, null); assertFalse(d.isDisposed()); d.dispose(); @@ -332,7 +403,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onNext(1); observer.onError(new TestException()); @@ -395,7 +466,7 @@ public Observable apply(Observable o) throws Exception { @Test public void disposeInOnNext() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); BehaviorSubject.createDefault(1) .debounce(new Function>() { @@ -413,12 +484,12 @@ public ObservableSource apply(Integer o) throws Exception { @Test public void disposedInOnComplete() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); to.dispose(); observer.onComplete(); } @@ -430,7 +501,7 @@ protected void subscribeActual(Observer observer) { @Test public void emitLate() { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); TestObserver to = Observable.range(1, 2) .debounce(new Function>() { @@ -442,7 +513,7 @@ public ObservableSource apply(Integer o) throws Exception { return new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }; @@ -469,13 +540,13 @@ public Publisher apply(Flowable f) @Test public void timedDisposedIgnoredBySource() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); new Observable() { @Override protected void subscribeActual( Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); to.dispose(); observer.onNext(1); observer.onComplete(); @@ -487,13 +558,13 @@ protected void subscribeActual( @Test public void timedLateEmit() { - TestObserver to = new TestObserver(); - DebounceTimedObserver sub = new DebounceTimedObserver( - to, 1, TimeUnit.SECONDS, new TestScheduler().createWorker()); + TestObserver to = new TestObserver<>(); + DebounceTimedObserver sub = new DebounceTimedObserver<>( + to, 1, TimeUnit.SECONDS, new TestScheduler().createWorker(), null); - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); - DebounceEmitter de = new DebounceEmitter(1, 50, sub); + DebounceEmitter de = new DebounceEmitter<>(1, 50, sub); de.run(); de.run(); @@ -517,4 +588,9 @@ public ObservableSource apply(Object o) { } }).subscribe(); } + + @Test + public void doubleOnSubscribeTime() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.debounce(1, TimeUnit.SECONDS)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefaultIfEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefaultIfEmptyTest.java index 1ca00cf1b4..7a1d66e908 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefaultIfEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDefaultIfEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDeferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDeferTest.java index 50ded2e55a..cb919f02ff 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDeferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDeferTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOtherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOtherTest.java index 604321a714..02d988c2e8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOtherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelaySubscriptionOtherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public class ObservableDelaySubscriptionOtherTest extends RxJavaTest { public void noPrematureSubscription() { PublishSubject other = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -65,7 +65,7 @@ public void accept(Disposable d) { public void noMultipleSubscriptions() { PublishSubject other = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -99,7 +99,7 @@ public void accept(Disposable d) { public void completeTriggersSubscription() { PublishSubject other = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -132,7 +132,7 @@ public void accept(Disposable d) { public void noPrematureSubscriptionToError() { PublishSubject other = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger subscribed = new AtomicInteger(); @@ -165,7 +165,7 @@ public void accept(Disposable d) { public void noSubscriptionIfOtherErrors() { PublishSubject other = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger subscribed = new AtomicInteger(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelayTest.java index cc93594bb8..778d2d4e64 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDelayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,6 +20,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.LockSupport; import org.junit.*; import org.mockito.InOrder; @@ -29,6 +30,7 @@ import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.schedulers.*; @@ -219,7 +221,7 @@ public void delaySubscriptionDisposeBeforeTime() { Observable result = Observable.just(1, 2, 3).delaySubscription(100, TimeUnit.MILLISECONDS, scheduler); Observer o = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); result.subscribe(to); to.dispose(); @@ -233,7 +235,7 @@ public void delaySubscriptionDisposeBeforeTime() { @Test public void delayWithObservableNormal1() { PublishSubject source = PublishSubject.create(); - final List> delays = new ArrayList>(); + final List> delays = new ArrayList<>(); final int n = 10; for (int i = 0; i < n; i++) { PublishSubject delay = PublishSubject.create(); @@ -584,7 +586,7 @@ public void delayWithObservableReorder() { int n = 3; PublishSubject source = PublishSubject.create(); - final List> subjects = new ArrayList>(); + final List> subjects = new ArrayList<>(); for (int i = 0; i < n; i++) { subjects.add(PublishSubject. create()); } @@ -632,7 +634,7 @@ public void accept(Notification t1) { } }); - TestObserver observer = new TestObserver(); + TestObserver observer = new TestObserver<>(); delayed.subscribe(observer); // all will be delivered after 500ms since range does not delay between them scheduler.advanceTimeBy(500L, TimeUnit.MILLISECONDS); @@ -641,7 +643,7 @@ public void accept(Notification t1) { @Test public void backpressureWithTimedDelay() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, Flowable.bufferSize() * 2) .delay(100, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) @@ -669,7 +671,7 @@ public Integer apply(Integer t) { @Test public void backpressureWithSubscriptionTimedDelay() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, Flowable.bufferSize() * 2) .delaySubscription(100, TimeUnit.MILLISECONDS) .delay(100, TimeUnit.MILLISECONDS) @@ -698,7 +700,7 @@ public Integer apply(Integer t) { @Test public void backpressureWithSelectorDelay() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, Flowable.bufferSize() * 2) .delay(new Function>() { @@ -733,7 +735,7 @@ public Integer apply(Integer t) { @Test public void backpressureWithSelectorDelayAndSubscriptionDelay() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, Flowable.bufferSize() * 2) .delay(Observable.timer(500, TimeUnit.MILLISECONDS) , new Function>() { @@ -773,7 +775,7 @@ public void errorRunsBeforeOnNext() { PublishSubject ps = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); ps.delay(1, TimeUnit.SECONDS, test).subscribe(to); @@ -796,7 +798,7 @@ public void delaySupplierSimple() { Observable source = Observable.range(1, 5); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.delaySubscription(ps).subscribe(to); @@ -817,7 +819,7 @@ public void delaySupplierCompletes() { Observable source = Observable.range(1, 5); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.delaySubscription(ps).subscribe(to); @@ -839,7 +841,7 @@ public void delaySupplierErrors() { Observable source = Observable.range(1, 5); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.delaySubscription(ps).subscribe(to); @@ -866,7 +868,7 @@ public void delayWithTimeDelayError() throws Exception { @Test public void onErrorCalledOnScheduler() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference thread = new AtomicReference(); + final AtomicReference thread = new AtomicReference<>(); Observable.error(new Exception()) .delay(0, TimeUnit.MILLISECONDS, Schedulers.newThread()) @@ -978,4 +980,37 @@ public Observable apply(Integer t) throws Exception { .to(TestHelper.testConsumer()) .assertFailureAndMessage(NullPointerException.class, "The itemDelay returned a null ObservableSource"); } -} + + @Test + public void cancelShouldPreventRandomSubsequentEmissions() { + for (int attempt = 1; attempt < 100; attempt ++) { + + SequentialDisposable disposable = new SequentialDisposable(); + ConcurrentLinkedQueue sink = new ConcurrentLinkedQueue<>(); + + disposable.replace( + Observable.range(1, 10) + .delay(1, TimeUnit.MICROSECONDS, Schedulers.computation(), true) + .doOnNext(v -> { + if (v == 1) { + Schedulers.computation().scheduleDirect(disposable::dispose); + } + sink.offer(v); + }) + .subscribe()); + + LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1)); + + Integer last = null; + + while (!sink.isEmpty()) { + Integer current = sink.poll(); + + if (last != null && last + 1 != current) { + fail("Emission hole: " + last + " -> " + current); + } + + last = current; + } + } + }} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java index 84a6b06b06..e28b71a629 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDematerializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; @@ -140,7 +140,7 @@ public void completePassThru() { Observer observer = TestHelper.mockObserver(); - TestObserverEx to = new TestObserverEx(observer); + TestObserverEx to = new TestObserverEx<>(observer); dematerialize.subscribe(to); System.out.println(to.errors()); @@ -202,7 +202,7 @@ public void eventsAfterDematerializedTerminal() { new Observable>() { @Override protected void subscribeActual(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(Notification.createOnComplete()); observer.onNext(Notification.createOnNext(1)); observer.onNext(Notification.createOnError(new TestException("First"))); @@ -221,16 +221,17 @@ protected void subscribeActual(Observer> observer) } @Test + @SuppressWarnings("unchecked") public void nonNotificationInstanceAfterDispose() { - new Observable>() { + new Observable() { @Override - protected void subscribeActual(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + protected void subscribeActual(Observer observer) { + observer.onSubscribe(Disposable.empty()); observer.onNext(Notification.createOnComplete()); - observer.onNext(Notification.createOnNext(1)); + observer.onNext(1); } } - .dematerialize(Functions.>identity()) + .dematerialize(v -> (Notification)v) .test() .assertResult(); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetachTest.java index ef67be319e..5e4e3d5fce 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDetachTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.observable; import java.lang.ref.WeakReference; @@ -33,9 +31,9 @@ public class ObservableDetachTest extends RxJavaTest { public void just() throws Exception { o = new Object(); - WeakReference wr = new WeakReference(o); + WeakReference wr = new WeakReference<>(o); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(o).count().toObservable().onTerminateDetach().subscribe(to); @@ -54,7 +52,7 @@ public void just() throws Exception { @Test public void error() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.error(new TestException()).onTerminateDetach().subscribe(to); @@ -65,7 +63,7 @@ public void error() { @Test public void empty() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.empty().onTerminateDetach().subscribe(to); @@ -76,7 +74,7 @@ public void empty() { @Test public void range() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 1000).onTerminateDetach().subscribe(to); @@ -89,7 +87,7 @@ public void range() { public void justUnsubscribed() throws Exception { o = new Object(); - WeakReference wr = new WeakReference(o); + WeakReference wr = new WeakReference<>(o); TestObserver to = Observable.just(o).count().toObservable().onTerminateDetach().test(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctTest.java index 2a9d889ba2..178f700db5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,8 +29,9 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.UnicastSubject; import io.reactivex.rxjava3.testsupport.*; @@ -115,7 +116,7 @@ public void error() { @Test public void fusedSync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.just(1, 1, 2, 1, 3, 2, 4, 5, 4) .distinct() @@ -127,7 +128,7 @@ public void fusedSync() { @Test public void fusedAsync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); UnicastSubject us = UnicastSubject.create(); @@ -205,7 +206,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChangedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChangedTest.java index f28a9fb18b..7f58ab2f89 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChangedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDistinctUntilChangedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,15 +19,15 @@ import java.io.IOException; import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import org.mockito.InOrder; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; @@ -148,7 +148,7 @@ public boolean test(String a, String b) { @Test public void fused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.just(1, 2, 2, 3, 3, 4, 5) .distinctUntilChanged(new BiPredicate() { @@ -167,11 +167,11 @@ public boolean test(Integer a, Integer b) throws Exception { @Test public void fusedAsync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - up + us .distinctUntilChanged(new BiPredicate() { @Override public boolean test(Integer a, Integer b) throws Exception { @@ -180,7 +180,7 @@ public boolean test(Integer a, Integer b) throws Exception { }) .subscribe(to); - TestHelper.emit(up, 1, 2, 2, 3, 3, 4, 5); + TestHelper.emit(us, 1, 2, 2, 3, 3, 4, 5); to.assertFuseable() .assertFusionMode(QueueFuseable.ASYNC) @@ -196,7 +196,7 @@ public void ignoreCancel() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onNext(3); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNextTest.java index f5f2e8feb6..8338d2c9ec 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoAfterNextTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,14 +24,14 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.subjects.UnicastSubject; import io.reactivex.rxjava3.testsupport.*; public class ObservableDoAfterNextTest extends RxJavaTest { - final List values = new ArrayList(); + final List values = new ArrayList<>(); final Consumer afterNext = new Consumer() { @Override @@ -101,7 +101,7 @@ public void empty() { @Test public void syncFused() { - TestObserverEx to0 = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to0 = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5) .doAfterNext(afterNext) @@ -115,7 +115,7 @@ public void syncFused() { @Test public void asyncFusedRejected() { - TestObserverEx to0 = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to0 = new TestObserverEx<>(QueueFuseable.ASYNC); Observable.range(1, 5) .doAfterNext(afterNext) @@ -129,13 +129,13 @@ public void asyncFusedRejected() { @Test public void asyncFused() { - TestObserverEx to0 = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to0 = new TestObserverEx<>(QueueFuseable.ASYNC); - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - TestHelper.emit(up, 1, 2, 3, 4, 5); + TestHelper.emit(us, 1, 2, 3, 4, 5); - up + us .doAfterNext(afterNext) .subscribe(to0); @@ -145,11 +145,6 @@ public void asyncFused() { assertEquals(Arrays.asList(-1, -2, -3, -4, -5), values); } - @Test(expected = NullPointerException.class) - public void consumerNull() { - Observable.just(1).doAfterNext(null); - } - @Test public void justConditional() { Observable.just(1) @@ -196,7 +191,7 @@ public void emptyConditional() { @Test public void syncFusedConditional() { - TestObserverEx to0 = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to0 = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5) .doAfterNext(afterNext) @@ -211,7 +206,7 @@ public void syncFusedConditional() { @Test public void asyncFusedRejectedConditional() { - TestObserverEx to0 = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to0 = new TestObserverEx<>(QueueFuseable.ASYNC); Observable.range(1, 5) .doAfterNext(afterNext) @@ -226,13 +221,13 @@ public void asyncFusedRejectedConditional() { @Test public void asyncFusedConditional() { - TestObserverEx to0 = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to0 = new TestObserverEx<>(QueueFuseable.ASYNC); - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - TestHelper.emit(up, 1, 2, 3, 4, 5); + TestHelper.emit(us, 1, 2, 3, 4, 5); - up + us .doAfterNext(afterNext) .filter(Functions.alwaysTrue()) .subscribe(to0); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java index 9fd860816a..3753b41127 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoFinallyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,8 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.UnicastSubject; import io.reactivex.rxjava3.testsupport.*; @@ -99,7 +100,7 @@ public Observable apply(Observable f) throws Exception { @Test public void syncFused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5) .doFinally(this) @@ -113,7 +114,7 @@ public void syncFused() { @Test public void syncFusedBoundary() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC | QueueFuseable.BOUNDARY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC | QueueFuseable.BOUNDARY); Observable.range(1, 5) .doFinally(this) @@ -127,12 +128,12 @@ public void syncFusedBoundary() { @Test public void asyncFused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC); - UnicastSubject up = UnicastSubject.create(); - TestHelper.emit(up, 1, 2, 3, 4, 5); + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); - up + us .doFinally(this) .subscribe(to); @@ -144,12 +145,12 @@ public void asyncFused() { @Test public void asyncFusedBoundary() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC | QueueFuseable.BOUNDARY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC | QueueFuseable.BOUNDARY); - UnicastSubject up = UnicastSubject.create(); - TestHelper.emit(up, 1, 2, 3, 4, 5); + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); - up + us .doFinally(this) .subscribe(to); @@ -206,7 +207,7 @@ public void normalTakeConditional() { @Test public void syncFusedConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5) .doFinally(this) @@ -221,7 +222,7 @@ public void syncFusedConditional() { @Test public void nonFused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5).hide() .doFinally(this) @@ -235,7 +236,7 @@ public void nonFused() { @Test public void nonFusedConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5).hide() .doFinally(this) @@ -250,7 +251,7 @@ public void nonFusedConditional() { @Test public void syncFusedBoundaryConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC | QueueFuseable.BOUNDARY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC | QueueFuseable.BOUNDARY); Observable.range(1, 5) .doFinally(this) @@ -265,12 +266,12 @@ public void syncFusedBoundaryConditional() { @Test public void asyncFusedConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC); - UnicastSubject up = UnicastSubject.create(); - TestHelper.emit(up, 1, 2, 3, 4, 5); + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); - up + us .doFinally(this) .filter(Functions.alwaysTrue()) .subscribe(to); @@ -283,12 +284,12 @@ public void asyncFusedConditional() { @Test public void asyncFusedBoundaryConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC | QueueFuseable.BOUNDARY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC | QueueFuseable.BOUNDARY); - UnicastSubject up = UnicastSubject.create(); - TestHelper.emit(up, 1, 2, 3, 4, 5); + UnicastSubject us = UnicastSubject.create(); + TestHelper.emit(us, 1, 2, 3, 4, 5); - up + us .doFinally(this) .filter(Functions.alwaysTrue()) .subscribe(to); @@ -299,11 +300,6 @@ public void asyncFusedBoundaryConditional() { assertEquals(1, calls); } - @Test(expected = NullPointerException.class) - public void nullAction() { - Observable.just(1).doFinally(null); - } - @Test public void actionThrows() { List errors = TestHelper.trackPluginErrors(); @@ -446,7 +442,7 @@ public void onComplete() { @Test public void eventOrdering() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.error(new TestException()) .doOnDispose(new Action() { @@ -486,7 +482,7 @@ public void run() throws Exception { @Test public void eventOrdering2() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.just(1) .doOnDispose(new Action() { @@ -524,4 +520,16 @@ public void run() throws Exception { assertEquals(Arrays.asList("onNext", "onComplete", "finally"), list); } + @Test + public void fusionRejected() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ANY); + + TestHelper.rejectObservableFusion() + .doFinally(() -> { }) + .subscribeWith(to); + + to.assertFuseable() + .assertFusionMode(QueueFuseable.NONE); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEachTest.java index 4205fad5e2..1f71d2b0a3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,15 +21,15 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subjects.UnicastSubject; import io.reactivex.rxjava3.testsupport.*; @@ -202,7 +202,7 @@ public void accept(List booleans) { @Test public void onErrorThrows() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.error(new TestException()) .doOnError(new Consumer() { @@ -232,7 +232,7 @@ public void ignoreCancel() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onError(new IOException()); @@ -262,7 +262,7 @@ public void onErrorAfterCrash() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException()); } }) @@ -289,7 +289,7 @@ public void onCompleteAfterCrash() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }) @@ -313,7 +313,7 @@ public void onCompleteCrash() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }) @@ -335,7 +335,7 @@ public void ignoreCancelConditional() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onError(new IOException()); @@ -366,7 +366,7 @@ public void onErrorAfterCrashConditional() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException()); } }) @@ -410,7 +410,7 @@ public void onCompleteAfterCrashConditional() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }) @@ -435,7 +435,7 @@ public void onCompleteCrashConditional() { Observable.wrap(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } }) @@ -472,7 +472,7 @@ public void accept(Throwable e) throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0, 0 }; @@ -502,7 +502,7 @@ public void run() throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fusedOnErrorCrash() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0 }; @@ -531,7 +531,7 @@ public void run() throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fusedConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0, 0 }; @@ -562,7 +562,7 @@ public void run() throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fusedOnErrorCrashConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0 }; @@ -592,13 +592,13 @@ public void run() throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fusedAsync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0, 0 }; - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - up + us .doOnNext(new Consumer() { @Override public void accept(Integer v) throws Exception { @@ -613,7 +613,7 @@ public void run() throws Exception { }) .subscribe(to); - TestHelper.emit(up, 1, 2, 3, 4, 5); + TestHelper.emit(us, 1, 2, 3, 4, 5); to.assertFuseable() .assertFusionMode(QueueFuseable.ASYNC) @@ -626,13 +626,13 @@ public void run() throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fusedAsyncConditional() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0, 0 }; - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - up + us .doOnNext(new Consumer() { @Override public void accept(Integer v) throws Exception { @@ -648,7 +648,7 @@ public void run() throws Exception { .filter(Functions.alwaysTrue()) .subscribe(to); - TestHelper.emit(up, 1, 2, 3, 4, 5); + TestHelper.emit(us, 1, 2, 3, 4, 5); to.assertFuseable() .assertFusionMode(QueueFuseable.ASYNC) @@ -661,13 +661,13 @@ public void run() throws Exception { @Test @Ignore("Fusion not supported yet") // TODO decide/implement fusion public void fusedAsyncConditional2() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); final int[] call = { 0, 0 }; - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - up.hide() + us.hide() .doOnNext(new Consumer() { @Override public void accept(Integer v) throws Exception { @@ -683,7 +683,7 @@ public void run() throws Exception { .filter(Functions.alwaysTrue()) .subscribe(to); - TestHelper.emit(up, 1, 2, 3, 4, 5); + TestHelper.emit(us, 1, 2, 3, 4, 5); to.assertFuseable() .assertFusionMode(QueueFuseable.NONE) @@ -695,7 +695,7 @@ public void run() throws Exception { @Test public void dispose() { - TestHelper.checkDisposed(Observable.just(1).doOnEach(new TestObserver())); + TestHelper.checkDisposed(Observable.just(1).doOnEach(new TestObserver<>())); } @Test @@ -703,7 +703,7 @@ public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeObservable(new Function, ObservableSource>() { @Override public ObservableSource apply(Observable o) throws Exception { - return o.doOnEach(new TestObserver()); + return o.doOnEach(new TestObserver<>()); } }); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnSubscribeTest.java index ef57c50225..f001d28ac0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnSubscribeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnSubscribeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -69,12 +69,12 @@ public void doOnUnSubscribeWorksWithRefCount() throws Exception { final AtomicInteger onSubscribed = new AtomicInteger(); final AtomicInteger countBefore = new AtomicInteger(); final AtomicInteger countAfter = new AtomicInteger(); - final AtomicReference> sref = new AtomicReference>(); + final AtomicReference> sref = new AtomicReference<>(); Observable o = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); onSubscribed.incrementAndGet(); sref.set(observer); } @@ -111,7 +111,7 @@ public void accept(Disposable d) { public void onSubscribeCrash() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable bs = Disposables.empty(); + final Disposable bs = Disposable.empty(); new Observable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnUnsubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnUnsubscribeTest.java index c90105fb04..5ac217725b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnUnsubscribeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableDoOnUnsubscribeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -68,11 +68,11 @@ public void run() { } }); - List subscriptions = new ArrayList(); - List> subscribers = new ArrayList>(); + List subscriptions = new ArrayList<>(); + List> subscribers = new ArrayList<>(); for (int i = 0; i < subCount; ++i) { - TestObserver observer = new TestObserver(); + TestObserver observer = new TestObserver<>(); subscriptions.add(observer); longs.subscribe(observer); subscribers.add(observer); @@ -131,11 +131,11 @@ public void run() { .publish() .refCount(); - List subscriptions = new ArrayList(); - List> subscribers = new ArrayList>(); + List subscriptions = new ArrayList<>(); + List> subscribers = new ArrayList<>(); for (int i = 0; i < subCount; ++i) { - TestObserver observer = new TestObserver(); + TestObserver observer = new TestObserver<>(); longs.subscribe(observer); subscriptions.add(observer); subscribers.add(observer); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtTest.java index a11255abf2..8ce5cb047c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableElementAtTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,12 +17,12 @@ import java.util.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -233,7 +233,7 @@ public void badSourceObservable() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); @@ -259,7 +259,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); @@ -284,7 +284,7 @@ public void badSource2() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilterTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilterTest.java index 22cd6c060a..71585b1c33 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilterTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFilterTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.subjects.UnicastSubject; import io.reactivex.rxjava3.testsupport.*; @@ -68,7 +68,7 @@ public ObservableSource apply(Observable o) throws Exception { @Test public void fusedSync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.range(1, 5) .filter(new Predicate() { @@ -85,7 +85,7 @@ public boolean test(Integer v) throws Exception { @Test public void fusedAsync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); UnicastSubject us = UnicastSubject.create(); @@ -106,7 +106,7 @@ public boolean test(Integer v) throws Exception { @Test public void fusedReject() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY | QueueFuseable.BOUNDARY); Observable.range(1, 5) .filter(new Predicate() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFinallyTest.java index 7767ddb9fc..9f20203ed2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFinallyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFirstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFirstTest.java index e022144ce2..9129f1d0cd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFirstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFirstTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java index b07a13a6de..facfb084d1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ import static org.junit.Assert.*; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import org.junit.Test; @@ -24,8 +24,9 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.testsupport.*; @@ -169,7 +170,7 @@ public CompletableSource apply(Integer v) throws Exception { @Test public void fusedObservable() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.range(1, 10) .flatMapCompletable(new Function() { @@ -334,7 +335,7 @@ public CompletableSource apply(Integer v) throws Exception { @Test public void fused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.range(1, 10) .flatMapCompletable(new Function() { @@ -372,7 +373,7 @@ public CompletableSource apply(Integer v) throws Exception { return new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -447,7 +448,7 @@ public CompletableSource apply(Integer v) throws Exception { return new Completable() { @Override protected void subscribeActual(CompletableObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -506,4 +507,59 @@ public Completable apply(Integer v) throws Throwable { } }); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.flatMapCompletable(v -> Completable.never()).toObservable()); + } + + @Test + public void doubleOnSubscribeCompletable() { + TestHelper.checkDoubleOnSubscribeObservableToCompletable(o -> o.flatMapCompletable(v -> Completable.never())); + } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Completable.complete(); + }) + .toObservable() + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } + + @Test + public void cancelWhileMappingCompletable() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapCompletable(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Completable.complete(); + }) + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java index fbd55ae32a..0c0d29c8e7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,14 +21,14 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; public class ObservableFlatMapMaybeTest extends RxJavaTest { @@ -325,7 +325,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -348,7 +348,7 @@ public void badInnerSource() { .flatMapMaybe(Functions.justFunction(new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -429,7 +429,7 @@ public MaybeSource apply(PublishSubject v) throws Exception { @Test public void disposeInner() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1).flatMapMaybe(new Function>() { @Override @@ -437,7 +437,7 @@ public MaybeSource apply(Integer v) throws Exception { return new Maybe() { @Override protected void subscribeActual(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -483,4 +483,64 @@ public Maybe apply(Integer v) throws Throwable { } }); } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapMaybe(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Maybe.just(1); + }) + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } + + @Test + public void successCompleteRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + MaybeSubject ms1 = MaybeSubject.create(); + MaybeSubject ms2 = MaybeSubject.create(); + + TestObserver to = Observable.just(1, 2) + .flatMapMaybe(v -> v == 1 ? ms1 : ms2) + .test(); + + TestHelper.race( + () -> ms1.onComplete(), + () -> ms2.onSuccess(1) + ); + + to.assertResult(1); + } + } + + @Test + public void successCompleteRace2() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + MaybeSubject ms1 = MaybeSubject.create(); + MaybeSubject ms2 = MaybeSubject.create(); + + TestObserver to = Observable.just(1, 2) + .flatMapMaybe(v -> v == 1 ? ms1 : ms2) + .test(); + + TestHelper.race( + () -> ms2.onSuccess(1), + () -> ms1.onComplete() + ); + + to.assertResult(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java index 77db13d5a1..1fa219111b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,14 +21,14 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; public class ObservableFlatMapSingleTest extends RxJavaTest { @@ -274,7 +274,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -297,7 +297,7 @@ public void badInnerSource() { .flatMapSingle(Functions.justFunction(new Single() { @Override protected void subscribeActual(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -344,7 +344,7 @@ public SingleSource apply(PublishSubject v) throws Exception { @Test public void disposeInner() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1).flatMapSingle(new Function>() { @Override @@ -352,7 +352,7 @@ public SingleSource apply(Integer v) throws Exception { return new Single() { @Override protected void subscribeActual(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); @@ -398,4 +398,69 @@ public Single apply(Integer v) throws Throwable { } }); } + + @Test + public void innerErrorOuterCompleteRace() { + TestException ex = new TestException(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + SingleSubject ps2 = SingleSubject.create(); + + TestObserver to = ps1.flatMapSingle(v -> ps2) + .test(); + + ps1.onNext(1); + + TestHelper.race( + () -> ps1.onComplete(), + () -> ps2.onError(ex) + ); + + to.assertFailure(TestException.class); + } + } + + @Test + public void cancelWhileMapping() throws Throwable { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + PublishSubject ps1 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + + ps1.flatMapSingle(v -> { + TestHelper.raceOther(() -> { + to.dispose(); + }, cdl); + return Single.just(1); + }) + .subscribe(to); + + ps1.onNext(1); + + cdl.await(); + } + } + + @Test + public void onNextDrainCancel() { + SingleSubject ss1 = SingleSubject.create(); + SingleSubject ss2 = SingleSubject.create(); + + TestObserver to = new TestObserver<>(); + + Observable.just(1, 2) + .flatMapSingle(v -> v == 1 ? ss1 : ss2) + .doOnNext(v -> { + if (v == 1) { + ss2.onSuccess(2); + to.dispose(); + } + }) + .subscribe(to); + + ss1.onSuccess(1); + + to.assertValuesOnly(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java index cf31f26c3a..4622888628 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,16 +17,18 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import org.junit.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -338,13 +340,13 @@ public Observable apply(Integer t1) { } }, m); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); - Set expected = new HashSet(Arrays.asList( + Set expected = new HashSet<>(Arrays.asList( 10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61, 70, 71, 80, 81, 90, 91, 100, 101 )); Assert.assertEquals(expected.size(), to.values().size()); @@ -369,13 +371,13 @@ public Integer apply(Integer t1, Integer t2) { } }, m); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); - Set expected = new HashSet(Arrays.asList( + Set expected = new HashSet<>(Arrays.asList( 1010, 1011, 2020, 2021, 3030, 3031, 4040, 4041, 5050, 5051, 6060, 6061, 7070, 7071, 8080, 8081, 9090, 9091, 10100, 10101 )); @@ -415,7 +417,7 @@ public void flatMapTransformsMaxConcurrentNormal() { Observable source = Observable.fromIterable(Arrays.asList(10, 20, 30)); Observer o = TestHelper.mockObserver(); - TestObserverEx to = new TestObserverEx(o); + TestObserverEx to = new TestObserverEx<>(o); Function> just = just(onError); source.flatMap(just(onNext), just, just0(onComplete), m).subscribe(to); @@ -440,7 +442,7 @@ public void flatMapRangeMixedAsyncLoop() { if (i % 10 == 0) { System.out.println("flatMapRangeAsyncLoop > " + i); } - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(0, 1000) .flatMap(new Function>() { final Random rnd = new Random(); @@ -464,7 +466,7 @@ public Observable apply(Integer t) { to.assertNoErrors(); List list = to.values(); if (list.size() < 1000) { - Set set = new HashSet(list); + Set set = new HashSet<>(list); for (int j = 0; j < 1000; j++) { if (!set.contains(j)) { System.out.println(j + " missing"); @@ -478,7 +480,7 @@ public Observable apply(Integer t) { @Test public void flatMapIntPassthruAsync() { for (int i = 0; i < 1000; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 1000).flatMap(new Function>() { @Override @@ -497,7 +499,7 @@ public Observable apply(Integer t) { @Test public void flatMapTwoNestedSync() { for (final int n : new int[] { 1, 1000, 1000000 }) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1, 2).flatMap(new Function>() { @Override @@ -972,10 +974,10 @@ public void onNext(Integer t) { @Test public void fusedSourceCrashResumeWithNextSource() { final UnicastSubject fusedSource = UnicastSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); ObservableFlatMap.MergeObserver merger = - new ObservableFlatMap.MergeObserver(to, new Function>() { + new ObservableFlatMap.MergeObserver<>(to, new Function>() { @Override public Observable apply(Integer t) throws Exception { @@ -984,7 +986,9 @@ public Observable apply(Integer t) .map(new Function() { @Override public Integer apply(Integer v) - throws Exception { throw new TestException(); } + throws Exception { + throw new TestException(); + } }) .compose(TestHelper.observableStripBoundary()); } @@ -992,7 +996,7 @@ public Integer apply(Integer v) } }, true, Integer.MAX_VALUE, 128); - merger.onSubscribe(Disposables.empty()); + merger.onSubscribe(Disposable.empty()); merger.getAndIncrement(); merger.onNext(0); @@ -1077,4 +1081,187 @@ public Observable apply(Integer v) throws Throwable { } }); } + + @Test + public void mainErrorsInnerCancelled() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + ps1 + .flatMap(v -> ps2) + .test(); + + ps1.onNext(1); + assertTrue("No subscribers?", ps2.hasObservers()); + + ps1.onError(new TestException()); + + assertFalse("Has subscribers?", ps2.hasObservers()); + } + + @Test + public void innerErrorsMainCancelled() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + ps1 + .flatMap(v -> ps2) + .test(); + + ps1.onNext(1); + assertTrue("No subscribers?", ps2.hasObservers()); + + ps2.onError(new TestException()); + + assertFalse("Has subscribers?", ps1.hasObservers()); + } + + @Test + public void signalsAfterMapperCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + observer.onSubscribe(Disposable.empty()); + observer.onNext(1); + observer.onNext(2); + observer.onComplete(); + observer.onError(new IOException()); + } + } + .flatMap(v -> { + throw new TestException(); + }) + .test() + .assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } + + @Test + public void scalarQueueTerminate() { + PublishSubject ps = PublishSubject.create(); + TestObserver to = new TestObserver<>(); + + ps + .flatMap(v -> Observable.just(v)) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + ps.onNext(3); + } + }) + .take(2) + .subscribe(to); + + ps.onNext(1); + + to.assertResult(1, 2); + } + + @Test + public void scalarQueueCompleteMain() throws Exception { + PublishSubject ps = PublishSubject.create(); + TestObserver to = new TestObserver<>(); + CountDownLatch cdl = new CountDownLatch(1); + ps + .flatMap(v -> Observable.just(v)) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + TestHelper.raceOther(() -> ps.onComplete(), cdl); + } + }) + .subscribe(to); + + ps.onNext(1); + + cdl.await(); + to.assertResult(1, 2); + } + + @Test + public void fusedInnerCrash() { + UnicastSubject us = UnicastSubject.create(); + PublishSubject ps = PublishSubject.create(); + + TestObserver to = Observable.just( + ps, + us.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + us.onNext(10); + } + }) + .test(); + + ps.onNext(1); + ps.onComplete(); + + to.assertFailure(TestException.class, 1, 2); + } + + @Test + public void fusedInnerCrash2() { + UnicastSubject us = UnicastSubject.create(); + PublishSubject ps = PublishSubject.create(); + + TestObserver to = Observable.just( + us.map(v -> { + if (v == 10) { + throw new TestException(); + } + return v; + }) + .compose(TestHelper.observableStripBoundary()) + , ps + ) + .flatMap(v -> v, true) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + us.onNext(10); + } + }) + .test(); + + ps.onNext(1); + ps.onComplete(); + + to.assertFailure(TestException.class, 1, 2); + } + + @Test(timeout = 5000) + public void mixedScalarAsync() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Observable + .range(0, 20) + .flatMap( + integer -> { + if (integer % 5 != 0) { + return Observable + .just(integer); + } + + return Observable + .just(-integer) + .observeOn(Schedulers.computation()); + }, + false, + 1 + ) + .ignoreElements() + .blockingAwait(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java index 2fdf6b24f3..15d6aae37b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFlattenIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -96,4 +96,9 @@ public void remove() { assertEquals(1, counter.get()); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.flatMapIterable(v -> Collections.singletonList(v))); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableForEachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableForEachTest.java index cd7273a282..ac8312a893 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableForEachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableForEachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public class ObservableForEachTest extends RxJavaTest { @Test public void forEachWile() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.range(1, 5) .doOnNext(new Consumer() { @@ -54,7 +54,7 @@ public boolean test(Integer v) throws Exception { @Test public void forEachWileWithError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.range(1, 5).concatWith(Observable.error(new TestException())) .doOnNext(new Consumer() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromActionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromActionTest.java new file mode 100644 index 0000000000..eb20166507 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromActionTest.java @@ -0,0 +1,214 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.*; + +public class ObservableFromActionTest extends RxJavaTest { + @Test + public void fromAction() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Observable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromActionTwice() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Action run = new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }; + + Observable.fromAction(run) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + + Observable.fromAction(run) + .test() + .assertResult(); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void fromActionInvokesLazy() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Observable source = Observable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }); + + assertEquals(0, atomicInteger.get()); + + source + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromActionThrows() { + Observable.fromAction(new Action() { + @Override + public void run() throws Exception { + throw new UnsupportedOperationException(); + } + }) + .test() + .assertFailure(UnsupportedOperationException.class); + } + + @SuppressWarnings("unchecked") + @Test + public void callable() throws Throwable { + final int[] counter = { 0 }; + + Observable m = Observable.fromAction(new Action() { + @Override + public void run() throws Exception { + counter[0]++; + } + }); + + assertTrue(m.getClass().toString(), m instanceof Supplier); + + assertNull(((Supplier)m).get()); + + assertEquals(1, counter[0]); + } + + @Test + public void noErrorLoss() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + TestObserver to = Observable.fromAction(new Action() { + @Override + public void run() throws Exception { + cdl1.countDown(); + cdl2.await(5, TimeUnit.SECONDS); + } + }).subscribeOn(Schedulers.single()).test(); + + assertTrue(cdl1.await(5, TimeUnit.SECONDS)); + + to.dispose(); + + int timeout = 10; + + while (timeout-- > 0 && errors.isEmpty()) { + Thread.sleep(100); + } + + TestHelper.assertUndeliverable(errors, 0, InterruptedException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void disposedUpfront() throws Throwable { + Action run = mock(Action.class); + + Observable.fromAction(run) + .test(true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void cancelWhileRunning() { + final TestObserver to = new TestObserver<>(); + + Observable.fromAction(new Action() { + @Override + public void run() throws Exception { + to.dispose(); + } + }) + .subscribeWith(to) + .assertEmpty(); + + assertTrue(to.isDisposed()); + } + + @Test + public void asyncFused() throws Throwable { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ASYNC); + + Action action = mock(Action.class); + + Observable.fromAction(action) + .subscribe(to); + + to.assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + + verify(action).run(); + } + + @Test + public void syncFusedRejected() throws Throwable { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Action action = mock(Action.class); + + Observable.fromAction(action) + .subscribe(to); + + to.assertFusionMode(QueueFuseable.NONE) + .assertResult(); + + verify(action).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallableTest.java index 060a871855..c701f60c47 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCallableTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.observable; @@ -121,7 +118,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromCallableObservable .subscribeOn(Schedulers.computation()) @@ -262,7 +259,7 @@ public Object call() throws Exception { @Test public void disposedOnCall() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.fromCallable(new Callable() { @Override @@ -280,7 +277,7 @@ public Integer call() throws Exception { public void disposedOnCallThrows() { List errors = TestHelper.trackPluginErrors(); try { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.fromCallable(new Callable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java new file mode 100644 index 0000000000..6cfd2eb0ad --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromCompletableTest.java @@ -0,0 +1,208 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.*; + +public class ObservableFromCompletableTest extends RxJavaTest { + @Test + public void fromCompletable() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Observable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + })) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromCompletableTwice() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Action run = new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + }; + + Observable.fromCompletable(Completable.fromAction(run)) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + + Observable.fromCompletable(Completable.fromAction(run)) + .test() + .assertResult(); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void fromCompletableInvokesLazy() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Observable source = Observable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + atomicInteger.incrementAndGet(); + } + })); + + assertEquals(0, atomicInteger.get()); + + source + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromCompletableThrows() { + Observable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + throw new UnsupportedOperationException(); + } + })) + .test() + .assertFailure(UnsupportedOperationException.class); + } + + @Test + public void noErrorLoss() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + TestObserver to = Observable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + cdl1.countDown(); + cdl2.await(5, TimeUnit.SECONDS); + } + })).subscribeOn(Schedulers.single()).test(); + + assertTrue(cdl1.await(5, TimeUnit.SECONDS)); + + to.dispose(); + + int timeout = 10; + + while (timeout-- > 0 && errors.isEmpty()) { + Thread.sleep(100); + } + + TestHelper.assertUndeliverable(errors, 0, InterruptedException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void disposedUpfront() throws Throwable { + Action run = mock(Action.class); + + Observable.fromCompletable(Completable.fromAction(run)) + .test(true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void cancelWhileRunning() { + final TestObserver to = new TestObserver<>(); + + Observable.fromCompletable(Completable.fromAction(new Action() { + @Override + public void run() throws Exception { + to.dispose(); + } + })) + .subscribeWith(to) + .assertEmpty(); + + assertTrue(to.isDisposed()); + } + + @Test + public void asyncFused() throws Throwable { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ASYNC); + + Action action = mock(Action.class); + + Observable.fromCompletable(Completable.fromAction(action)) + .subscribe(to); + + to.assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + + verify(action).run(); + } + + @Test + public void syncFusedRejected() throws Throwable { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Action action = mock(Action.class); + + Observable.fromCompletable(Completable.fromAction(action)) + .subscribe(to); + + to.assertFusionMode(QueueFuseable.NONE) + .assertResult(); + + verify(action).run(); + } + + @Test + public void disposed() { + TestHelper.checkDisposed(Observable.fromCompletable(Completable.never())); + } + + @Test + public void upstream() { + Observable o = Observable.fromCompletable(Completable.never()); + assertTrue(o instanceof HasUpstreamCompletableSource); + assertSame(Completable.never(), ((HasUpstreamCompletableSource)o).source()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java index 4cd33b78e9..b816baf4b0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,18 +29,14 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; import io.reactivex.rxjava3.observers.*; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.*; public class ObservableFromIterableTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void nullValue() { - Observable.fromIterable(null); - } - @Test public void listIterable() { Observable o = Observable.fromIterable(Arrays. asList("one", "two", "three")); @@ -119,7 +115,7 @@ public void observableFromIterable() { public void noBackpressure() { Observable o = Observable.fromIterable(Arrays.asList(1, 2, 3, 4, 5)); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); o.subscribe(to); @@ -132,7 +128,7 @@ public void subscribeMultipleTimes() { Observable o = Observable.fromIterable(Arrays.asList(1, 2, 3)); for (int i = 0; i < 10; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o.subscribe(to); @@ -235,7 +231,7 @@ public void onNext(Integer t) { @Test public void fusionWithConcatMap() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.fromIterable(Arrays.asList(1, 2, 3, 4)).concatMap( new Function>() { @@ -266,7 +262,7 @@ public void hasNext2Throws() { @Test public void hasNextCancels() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.fromIterable(new Iterable() { @Override @@ -303,7 +299,7 @@ public void remove() { @Test public void fusionRejected() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC); Observable.fromIterable(Arrays.asList(1, 2, 3)) .subscribe(to); @@ -350,4 +346,28 @@ public void onComplete() { } }); } + + @Test + public void disposeAfterHasNext() { + TestObserver to = new TestObserver<>(); + + Observable.fromIterable(() -> new Iterator() { + int count; + @Override + public boolean hasNext() { + if (count++ == 2) { + to.dispose(); + return false; + } + return true; + } + + @Override + public Integer next() { + return 1; + } + }) + .subscribeWith(to) + .assertValuesOnly(1, 1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromMaybeTest.java new file mode 100644 index 0000000000..e0175b4a59 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromMaybeTest.java @@ -0,0 +1,93 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.MaybeSubject; +import io.reactivex.rxjava3.testsupport.TestObserverEx; + +public class ObservableFromMaybeTest extends RxJavaTest { + + @Test + public void success() { + Observable.fromMaybe(Maybe.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Observable.fromMaybe(Maybe.empty().hide()) + .test() + .assertResult(); + } + + @Test + public void error() { + Observable.fromMaybe(Maybe.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelComposes() { + MaybeSubject ms = MaybeSubject.create(); + + TestObserver to = Observable.fromMaybe(ms) + .test(); + + to.assertEmpty(); + + assertTrue(ms.hasObservers()); + + to.dispose(); + + assertFalse(ms.hasObservers()); + } + + @Test + public void asyncFusion() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ASYNC); + + Observable.fromMaybe(Maybe.just(1)) + .subscribe(to); + + to + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void syncFusionRejected() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Observable.fromMaybe(Maybe.just(1)) + .subscribe(to); + + to + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromRunnableTest.java new file mode 100644 index 0000000000..b566f66775 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromRunnableTest.java @@ -0,0 +1,220 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Supplier; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.*; + +public class ObservableFromRunnableTest extends RxJavaTest { + @Test + public void fromRunnable() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Observable.fromRunnable(new Runnable() { + @Override + public void run() { + atomicInteger.incrementAndGet(); + } + }) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromRunnableTwice() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Runnable run = new Runnable() { + @Override + public void run() { + atomicInteger.incrementAndGet(); + } + }; + + Observable.fromRunnable(run) + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + + Observable.fromRunnable(run) + .test() + .assertResult(); + + assertEquals(2, atomicInteger.get()); + } + + @Test + public void fromRunnableInvokesLazy() { + final AtomicInteger atomicInteger = new AtomicInteger(); + + Observable source = Observable.fromRunnable(new Runnable() { + @Override + public void run() { + atomicInteger.incrementAndGet(); + } + }); + + assertEquals(0, atomicInteger.get()); + + source + .test() + .assertResult(); + + assertEquals(1, atomicInteger.get()); + } + + @Test + public void fromRunnableThrows() { + Observable.fromRunnable(new Runnable() { + @Override + public void run() { + throw new UnsupportedOperationException(); + } + }) + .test() + .assertFailure(UnsupportedOperationException.class); + } + + @SuppressWarnings("unchecked") + @Test + public void callable() throws Throwable { + final int[] counter = { 0 }; + + Observable m = Observable.fromRunnable(new Runnable() { + @Override + public void run() { + counter[0]++; + } + }); + + assertTrue(m.getClass().toString(), m instanceof Supplier); + + assertNull(((Supplier)m).get()); + + assertEquals(1, counter[0]); + } + + @Test + public void noErrorLoss() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + final CountDownLatch cdl1 = new CountDownLatch(1); + final CountDownLatch cdl2 = new CountDownLatch(1); + + TestObserver to = Observable.fromRunnable(new Runnable() { + @Override + public void run() { + cdl1.countDown(); + try { + cdl2.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new TestException(e); + } + } + }).subscribeOn(Schedulers.single()).test(); + + assertTrue(cdl1.await(5, TimeUnit.SECONDS)); + + to.dispose(); + + int timeout = 10; + + while (timeout-- > 0 && errors.isEmpty()) { + Thread.sleep(100); + } + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void disposedUpfront() throws Throwable { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run) + .test(true) + .assertEmpty(); + + verify(run, never()).run(); + } + + @Test + public void cancelWhileRunning() { + final TestObserver to = new TestObserver<>(); + + Observable.fromRunnable(new Runnable() { + @Override + public void run() { + to.dispose(); + } + }) + .subscribeWith(to) + .assertEmpty(); + + assertTrue(to.isDisposed()); + } + + @Test + public void asyncFused() throws Throwable { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ASYNC); + + Runnable action = mock(Runnable.class); + + Observable.fromRunnable(action) + .subscribe(to); + + to.assertFusionMode(QueueFuseable.ASYNC) + .assertResult(); + + verify(action).run(); + } + + @Test + public void syncFusedRejected() throws Throwable { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Runnable action = mock(Runnable.class); + + Observable.fromRunnable(action) + .subscribe(to); + + to.assertFusionMode(QueueFuseable.NONE) + .assertResult(); + + verify(action).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSingleTest.java new file mode 100644 index 0000000000..aaabb513b3 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSingleTest.java @@ -0,0 +1,86 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestObserverEx; + +public class ObservableFromSingleTest extends RxJavaTest { + + @Test + public void success() { + Observable.fromSingle(Single.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void error() { + Observable.fromSingle(Single.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelComposes() { + SingleSubject ms = SingleSubject.create(); + + TestObserver to = Observable.fromSingle(ms) + .test(); + + to.assertEmpty(); + + assertTrue(ms.hasObservers()); + + to.dispose(); + + assertFalse(ms.hasObservers()); + } + + @Test + public void asyncFusion() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.ASYNC); + + Observable.fromSingle(Single.just(1)) + .subscribe(to); + + to + .assertFuseable() + .assertFusionMode(QueueFuseable.ASYNC) + .assertResult(1); + } + + @Test + public void syncFusionRejected() { + TestObserverEx to = new TestObserverEx<>(); + to.setInitialFusionMode(QueueFuseable.SYNC); + + Observable.fromSingle(Single.just(1)) + .subscribe(to); + + to + .assertFuseable() + .assertFusionMode(QueueFuseable.NONE) + .assertResult(1); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplierTest.java index bf6e9d4e3b..56e31ddfb7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromSupplierTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.operators.observable; @@ -121,7 +118,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromSupplierObservable .subscribeOn(Schedulers.computation()) @@ -262,7 +259,7 @@ public Object get() throws Exception { @Test public void disposedOnCall() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.fromSupplier(new Supplier() { @Override @@ -280,7 +277,7 @@ public Integer get() throws Exception { public void disposedOnCallThrows() { List errors = TestHelper.trackPluginErrors(); try { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.fromSupplier(new Supplier() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromTest.java index 778520fa4f..639d3c4d14 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableFromTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.ScalarSupplier; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -30,7 +31,8 @@ public class ObservableFromTest extends RxJavaTest { @Test public void fromFutureTimeout() throws Exception { Observable.fromFuture(Observable.never() - .toFuture(), 100, TimeUnit.MILLISECONDS, Schedulers.io()) + .toFuture(), 100, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) .test() .awaitDone(5, TimeUnit.SECONDS) .assertFailure(TimeoutException.class); @@ -77,7 +79,7 @@ public ObservableSource apply(Flowable f) throws Exception { @Test public void fusionRejected() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC); Observable.fromArray(1, 2, 3) .subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java index 19735a3ad4..90a3d60eaf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGenerateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -194,4 +194,17 @@ public void accept(Emitter e) throws Exception { .test() .assertResult(); } + + @Test + public void onNextAfterOnComplete() { + Observable.generate(new Consumer>() { + @Override + public void accept(Emitter e) throws Exception { + e.onComplete(); + e.onNext(1); + } + }) + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java index 14ff56a665..32c1abc2e2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupByTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,14 +27,14 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observables.GroupedObservable; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.*; public class ObservableGroupByTest extends RxJavaTest { @@ -96,6 +96,7 @@ public void empty() { } @Test + @SuppressUndeliverable public void error() { Observable sourceStrings = Observable.just("one", "two", "three", "four", "five", "six"); Observable errorSource = Observable.error(new RuntimeException("forced failure")); @@ -105,7 +106,7 @@ public void error() { final AtomicInteger groupCounter = new AtomicInteger(); final AtomicInteger eventCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); grouped.flatMap(new Function, Observable>() { @@ -148,13 +149,13 @@ public void onNext(String v) { private static Map> toMap(Observable> observable) { - final ConcurrentHashMap> result = new ConcurrentHashMap>(); + final ConcurrentHashMap> result = new ConcurrentHashMap<>(); observable.doOnNext(new Consumer>() { @Override public void accept(final GroupedObservable o) { - result.put(o.getKey(), new ConcurrentLinkedQueue()); + result.put(o.getKey(), new ConcurrentLinkedQueue<>()); o.subscribe(new Consumer() { @Override @@ -188,7 +189,7 @@ public void groupedEventStream() throws Throwable { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); System.out.println("*** Subscribing to EventStream ***"); subscribeCounter.incrementAndGet(); new Thread(new Runnable() { @@ -597,12 +598,12 @@ public void accept(String s) { @Test public void firstGroupsCompleteAndParentSlowToThenEmitFinalGroupsAndThenComplete() throws InterruptedException { final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer sub) { - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.onNext(1); sub.onNext(2); sub.onNext(1); @@ -676,12 +677,12 @@ public void accept(String s) { public void firstGroupsCompleteAndParentSlowToThenEmitFinalGroupsWhichThenSubscribesOnAndDelaysAndThenCompletes() throws InterruptedException { System.err.println("----------------------------------------------------------------------------------------------"); final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer sub) { - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.onNext(1); sub.onNext(2); sub.onNext(1); @@ -768,12 +769,12 @@ public void accept(String s) { @Test public void firstGroupsCompleteAndParentSlowToThenEmitFinalGroupsWhichThenObservesOnAndDelaysAndThenCompletes() throws InterruptedException { final CountDownLatch first = new CountDownLatch(2); // there are two groups to first complete - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer sub) { - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.onNext(1); sub.onNext(2); sub.onNext(1); @@ -845,12 +846,12 @@ public void accept(String s) { @Test public void groupsWithNestedSubscribeOn() throws InterruptedException { - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer sub) { - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.onNext(1); sub.onNext(2); sub.onNext(1); @@ -902,12 +903,12 @@ public void accept(String s) { @Test public void groupsWithNestedObserveOn() throws InterruptedException { - final ArrayList results = new ArrayList(); + final ArrayList results = new ArrayList<>(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer sub) { - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); sub.onNext(1); sub.onNext(2); sub.onNext(1); @@ -968,7 +969,7 @@ Observable SYNC_INFINITE_OBSERVABLE_OF_EVENT(final int numGroups, final A @Override public void subscribe(final Observer op) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); op.onSubscribe(d); subscribeCounter.incrementAndGet(); int i = 0; @@ -1027,7 +1028,7 @@ public Boolean apply(Integer n) { @Test public void groupByBackpressure() throws InterruptedException { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 4000) .groupBy(IS_EVEN2) @@ -1154,7 +1155,7 @@ public String apply(String v) { } }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); m.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); System.out.println("ts .get " + to.values()); @@ -1170,7 +1171,7 @@ public void keySelectorThrows() { Observable m = source.groupBy(fail(0), dbl).flatMap(FLATTEN_INTEGER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); m.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); assertEquals(1, to.errors().size()); @@ -1178,11 +1179,12 @@ public void keySelectorThrows() { } @Test + @SuppressUndeliverable public void valueSelectorThrows() { Observable source = Observable.just(0, 1, 2, 3, 4, 5, 6); Observable m = source.groupBy(identity, fail(0)).flatMap(FLATTEN_INTEGER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); m.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); assertEquals(1, to.errors().size()); @@ -1196,7 +1198,7 @@ public void innerEscapeCompleted() { Observable m = source.groupBy(identity, dbl).flatMap(FLATTEN_INTEGER); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); m.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -1210,7 +1212,7 @@ public void innerEscapeCompleted() { public void exceptionIfSubscribeToChildMoreThanOnce() { Observable source = Observable.just(0); - final AtomicReference> inner = new AtomicReference>(); + final AtomicReference> inner = new AtomicReference<>(); Observable> m = source.groupBy(identity, dbl); @@ -1233,13 +1235,14 @@ public void accept(GroupedObservable t1) { } @Test + @SuppressUndeliverable public void error2() { Observable source = Observable.concat(Observable.just(0), Observable. error(new TestException("Forced failure"))); Observable m = source.groupBy(identity, dbl).flatMap(FLATTEN_INTEGER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); m.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); assertEquals(1, to.errors().size()); @@ -1248,7 +1251,7 @@ public void error2() { @Test public void groupByBackpressure3() throws InterruptedException { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 4000).groupBy(IS_EVEN2).flatMap(new Function, Observable>() { @@ -1305,7 +1308,7 @@ public void accept(Notification t1) { @Test public void groupByBackpressure2() throws InterruptedException { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 4000).groupBy(IS_EVEN2).flatMap(new Function, Observable>() { @@ -1346,7 +1349,7 @@ public Observable apply(GroupedObservable t) { @Test public void groupByWithNullKey() { final String[] key = new String[]{"uninitialized"}; - final List values = new ArrayList(); + final List values = new ArrayList<>(); Observable.just("a", "b", "c").groupBy(new Function() { @Override @@ -1382,7 +1385,7 @@ public void subscribe(Observer observer) { } } ); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o.groupBy(new Function() { @@ -1400,11 +1403,11 @@ public Integer apply(Integer integer) { @Test public void groupByShouldPropagateError() { final Throwable e = new RuntimeException("Oops"); - final TestObserverEx inner1 = new TestObserverEx(); - final TestObserverEx inner2 = new TestObserverEx(); + final TestObserverEx inner1 = new TestObserverEx<>(); + final TestObserverEx inner2 = new TestObserverEx<>(); final TestObserverEx> outer - = new TestObserverEx>(new DefaultObserver>() { + = new TestObserverEx<>(new DefaultObserver>() { @Override public void onComplete() { @@ -1427,7 +1430,7 @@ public void onNext(GroupedObservable o) { new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(0); observer.onNext(1); observer.onError(e); @@ -1446,6 +1449,7 @@ public Integer apply(Integer i) { } @Test + @SuppressUndeliverable public void keySelectorAndDelayError() { Observable.just(1).concatWith(Observable.error(new TestException())) .groupBy(Functions.identity(), true) @@ -1460,6 +1464,7 @@ public ObservableSource apply(GroupedObservable g) th } @Test + @SuppressUndeliverable public void keyAndValueSelectorAndDelayError() { Observable.just(1).concatWith(Observable.error(new TestException())) .groupBy(Functions.identity(), Functions.identity(), true) @@ -1546,7 +1551,7 @@ public void delayErrorSimpleComplete() { public void cancelOverFlatmapRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final PublishSubject ps = PublishSubject.create(); @@ -1589,7 +1594,7 @@ public void run() { @Test public void abandonedGroupsNoDataloss() { - final List> groups = new ArrayList>(); + final List> groups = new ArrayList<>(); Observable.range(1, 1000) .groupBy(new Function() { @@ -1618,8 +1623,8 @@ public void accept(GroupedObservable v) throws Throwable { @Test public void newGroupValueSelectorFails() { - TestObserver to1 = new TestObserver(); - final TestObserver to2 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); + final TestObserver to2 = new TestObserver<>(); Observable.just(1) .groupBy(Functions.identity(), new Function() { @@ -1645,8 +1650,8 @@ public void accept(GroupedObservable g) throws Throwable { @Test public void existingGroupValueSelectorFails() { - TestObserver to1 = new TestObserver(); - final TestObserver to2 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); + final TestObserver to2 = new TestObserver<>(); Observable.just(1, 2) .groupBy(Functions.justFunction(1), new Function() { @@ -1672,4 +1677,68 @@ public void accept(GroupedObservable g) throws Throwable { to2.assertFailure(TestException.class, 1); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.groupBy(v -> v)); + } + + @Test + public void nullKeyDisposeGroup() { + Observable.just(1) + .groupBy(v -> null) + .flatMap(v -> v.take(1)) + .test() + .assertResult(1); + } + + @Test + public void groupSubscribeOnNextRace() throws Throwable { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + BehaviorSubject bs = BehaviorSubject.createDefault(1); + CountDownLatch cdl = new CountDownLatch(1); + + bs.groupBy(v -> 1) + .doOnNext(g -> { + TestHelper.raceOther(() -> { + g.test(); + }, cdl); + }) + .test(); + + cdl.await(); + } + } + + @Test + public void abandonedGroupDispose() { + AtomicReference> ref = new AtomicReference<>(); + + Observable.just(1) + .groupBy(v -> 1) + .doOnNext(ref::set) + .test(); + + ref.get().take(1).test().assertResult(1); + } + + @Test + public void delayErrorCompleteMoreWorkInGroup() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.groupBy(v -> 1, true) + .flatMap(g -> g.doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + ps.onComplete(); + } + }) + ) + .test() + ; + + ps.onNext(1); + + to.assertResult(1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java index 5998e37a64..7959f90a05 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableGroupJoinTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.observable; import static org.junit.Assert.*; @@ -81,7 +79,7 @@ public Integer apply(Integer rightValue) throws Throwable { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test @@ -479,6 +477,7 @@ public Observable apply(Integer r, Observable l) throws Except } @Test + @SuppressUndeliverable public void innerErrorRight() { Observable.just(1) .groupJoin( @@ -726,4 +725,42 @@ public void leftRightEndState() { verify(js).innerClose(false, o); } + + @Test + public void disposeAfterOnNext() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + + ps1.groupJoin(ps2, v -> Observable.never(), v -> Observable.never(), (a, b) -> a) + .doOnNext(v -> { + to.dispose(); + }) + .subscribe(to); + + ps2.onNext(1); + ps1.onNext(1); + } + + @Test + public void completeWithMoreWork() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = new TestObserver<>(); + + ps1.groupJoin(ps2, v -> Observable.never(), v -> Observable.never(), (a, b) -> a) + .doOnNext(v -> { + if (v == 1) { + ps2.onNext(2); + ps1.onComplete(); + ps2.onComplete(); + } + }) + .subscribe(to); + + ps2.onNext(1); + ps1.onNext(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHideTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHideTest.java index 45361b1aed..0b624621ec 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHideTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableHideTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsTest.java index e5704dfd46..c7597c3a9f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIgnoreElementsTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -58,7 +58,7 @@ public void accept(Integer t) { @Test public void completedOkObservable() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(1, 10).ignoreElements().toObservable().subscribe(to); to.assertNoErrors(); to.assertNoValues(); @@ -67,7 +67,7 @@ public void completedOkObservable() { @Test public void errorReceivedObservable() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); TestException ex = new TestException("boo"); Observable.error(ex).ignoreElements().toObservable().subscribe(to); to.assertNoValues(); @@ -120,7 +120,7 @@ public void accept(Integer t) { @Test public void completedOk() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(1, 10).ignoreElements().subscribe(to); to.assertNoErrors(); to.assertNoValues(); @@ -129,7 +129,7 @@ public void completedOk() { @Test public void errorReceived() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); TestException ex = new TestException("boo"); Observable.error(ex).ignoreElements().subscribe(to); to.assertNoValues(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelperTest.java index 9c02f95ebb..87fc866f74 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableInternalHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.observable; import static org.junit.Assert.*; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java index 95f26637bf..637d9164be 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalRangeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -85,4 +85,12 @@ public void cancel() { .test() .assertResult(0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L); } + + @Test + public void takeSameAsRange() { + Observable.intervalRange(0, 2, 1, 1, TimeUnit.MILLISECONDS, Schedulers.trampoline()) + .take(2) + .test() + .assertResult(0L, 1L); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalTest.java index 11fe98a669..e8126962de 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableIntervalTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ public void cancel() { @Test public void cancelledOnRun() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); IntervalObserver is = new IntervalObserver(to); to.onSubscribe(is); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java index f10e62016d..c6f439cdcb 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableJoinTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.operators.observable; import static org.mockito.ArgumentMatchers.any; @@ -20,11 +18,11 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import org.mockito.MockitoAnnotations; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -54,7 +52,7 @@ public Observable apply(Integer t1) { @Before public void before() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); } @Test @@ -387,7 +385,7 @@ public void badOuterSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onError(new TestException("Second")); } @@ -424,7 +422,7 @@ public void badEndSource() { @Override protected void subscribeActual(Observer observer) { o[0] = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); } }), @@ -446,4 +444,27 @@ public Integer apply(Integer a, Integer b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void bothTerminateWithWorkRemaining() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = ps1.join( + ps2, + v -> Observable.never(), + v -> Observable.never(), + (a, b) -> a + b) + .doOnNext(v -> { + ps1.onComplete(); + ps2.onNext(2); + ps2.onComplete(); + }) + .test(); + + ps1.onNext(0); + ps2.onNext(1); + + to.assertComplete(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastTest.java index 0bd5868332..cabed665f4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java index 325fc504ae..b761a6ad02 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableLiftTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,12 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class ObservableLiftTest extends RxJavaTest { @Test + @SuppressUndeliverable public void callbackCrash() { try { Observable.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotificationTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotificationTest.java index ee8931853f..0672f528c9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotificationTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapNotificationTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,10 +13,10 @@ package io.reactivex.rxjava3.internal.operators.observable; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -27,7 +27,7 @@ public class ObservableMapNotificationTest extends RxJavaTest { @Test public void just() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1) .flatMap( new Function>() { @@ -67,7 +67,7 @@ protected void subscribeActual(Observer observer) { Functions.justFunction(Observable.just(2)), Functions.justSupplier(Observable.just(3)) ); - mn.onSubscribe(Disposables.empty()); + mn.onSubscribe(Disposable.empty()); } }); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapTest.java index da5651214f..4919a74f0d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.UnicastSubject; import io.reactivex.rxjava3.testsupport.*; @@ -306,7 +306,7 @@ public Integer apply(Integer i) { // } private static Map getMap(String prefix) { - Map m = new HashMap(); + Map m = new HashMap<>(); m.put("firstName", prefix + "First"); m.put("lastName", prefix + "Last"); return m; @@ -350,7 +350,7 @@ public ObservableSource apply(Observable o) throws Exception { @Test public void fusedSync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.range(1, 5) .map(Functions.identity()) @@ -362,7 +362,7 @@ public void fusedSync() { @Test public void fusedAsync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); UnicastSubject us = UnicastSubject.create(); @@ -378,7 +378,7 @@ public void fusedAsync() { @Test public void fusedReject() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY | QueueFuseable.BOUNDARY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY | QueueFuseable.BOUNDARY); Observable.range(1, 5) .map(Functions.identity()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterializeTest.java index 4549f6c514..02b16ad6ce 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMaterializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,12 +18,12 @@ import java.util.*; import java.util.concurrent.ExecutionException; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.DefaultObserver; import io.reactivex.rxjava3.testsupport.*; @@ -102,7 +102,7 @@ public void multipleSubscribes() throws InterruptedException, ExecutionException @Test public void withCompletionCausingError() { - TestObserverEx> to = new TestObserverEx>(); + TestObserverEx> to = new TestObserverEx<>(); final RuntimeException ex = new RuntimeException("boo"); Observable.empty().materialize().doOnNext(new Consumer() { @Override @@ -119,7 +119,7 @@ private static class TestLocalObserver extends DefaultObserver> notifications = new Vector>(); + List> notifications = new Vector<>(); @Override public void onComplete() { @@ -150,7 +150,7 @@ private static class TestAsyncErrorObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); t = new Thread(new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeDelayErrorTest.java index 82fd2e28fa..da92bc0338 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeDelayErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeDelayErrorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,12 +20,12 @@ import java.util.*; import java.util.concurrent.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.observers.DefaultObserver; import io.reactivex.rxjava3.testsupport.*; @@ -219,7 +219,7 @@ public void mergeObservableOfObservables() { @Override public void subscribe(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); // simulate what would happen in an Observable observer.onNext(o1); observer.onNext(o2); @@ -252,7 +252,7 @@ public void mergeArray() { public void mergeList() { final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); - List> listOfObservables = new ArrayList>(); + List> listOfObservables = new ArrayList<>(); listOfObservables.add(o1); listOfObservables.add(o2); @@ -317,7 +317,7 @@ private static class TestSynchronousObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("hello"); observer.onComplete(); } @@ -328,7 +328,7 @@ private static class TestASynchronousObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); t = new Thread(new Runnable() { @Override @@ -352,7 +352,7 @@ private static class TestErrorObservable implements ObservableSource { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); boolean errorThrown = false; for (String s : valuesToReturn) { if (s == null) { @@ -383,7 +383,7 @@ private static class TestAsyncErrorObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); t = new Thread(new Runnable() { @Override @@ -433,7 +433,7 @@ public void onNext(String args) { @Test public void errorInParentObservable() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.mergeDelayError( Observable.just(Observable.just(1), Observable.just(2)) .startWithItem(Observable. error(new RuntimeException())) @@ -453,7 +453,7 @@ public void errorInParentObservableDelayed() throws Exception { Observable> parentObservable = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> op) { - op.onSubscribe(Disposables.empty()); + op.onSubscribe(Disposable.empty()); op.onNext(Observable.unsafeCreate(o1)); op.onNext(Observable.unsafeCreate(o2)); op.onError(new NullPointerException("throwing exception in parent")); @@ -462,7 +462,7 @@ public void subscribe(Observer> op) { Observer stringObserver = TestHelper.mockObserver(); - TestObserverEx to = new TestObserverEx(stringObserver); + TestObserverEx to = new TestObserverEx<>(stringObserver); Observable m = Observable.mergeDelayError(parentObservable); m.subscribe(to); System.out.println("testErrorInParentObservableDelayed | " + i); @@ -480,7 +480,7 @@ private static class TestASynchronous1sDelayedObservable implements ObservableSo @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); t = new Thread(new Runnable() { @Override @@ -499,7 +499,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayError() { Observable.mergeDelayError(Arrays.asList(Observable.just(1), Observable.just(2))) @@ -507,7 +506,6 @@ public void mergeIterableDelayError() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeArrayDelayError() { Observable.mergeArrayDelayError(Observable.just(1), Observable.just(2)) @@ -515,7 +513,6 @@ public void mergeArrayDelayError() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorWithError() { Observable.mergeDelayError( @@ -561,7 +558,6 @@ public void mergeDelayErrorWithErrorMaxConcurrency() { .assertFailure(TestException.class, 1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorMaxConcurrency() { Observable.mergeDelayError( @@ -571,7 +567,6 @@ public void mergeIterableDelayErrorMaxConcurrency() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayErrorWithErrorMaxConcurrency() { Observable.mergeDelayError( diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeMaxConcurrentTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeMaxConcurrentTest.java index 8132fbe229..e1388f39ad 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeMaxConcurrentTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeMaxConcurrentTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,12 +19,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.internal.schedulers.IoScheduler; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -42,14 +42,14 @@ public void before() { @Test public void whenMaxConcurrentIsOne() { for (int i = 0; i < 100; i++) { - List> os = new ArrayList>(); + List> os = new ArrayList<>(); os.add(Observable.just("one", "two", "three", "four", "five").subscribeOn(Schedulers.newThread())); os.add(Observable.just("one", "two", "three", "four", "five").subscribeOn(Schedulers.newThread())); os.add(Observable.just("one", "two", "three", "four", "five").subscribeOn(Schedulers.newThread())); List expected = Arrays.asList("one", "two", "three", "four", "five", "one", "two", "three", "four", "five", "one", "two", "three", "four", "five"); Iterator iter = Observable.merge(os, 1).blockingIterable().iterator(); - List actual = new ArrayList(); + List actual = new ArrayList<>(); while (iter.hasNext()) { actual.add(iter.next()); } @@ -65,8 +65,8 @@ public void maxConcurrent() { int maxConcurrent = 2 + (times % 10); AtomicInteger subscriptionCount = new AtomicInteger(0); - List> os = new ArrayList>(); - List scos = new ArrayList(); + List> os = new ArrayList<>(); + List scos = new ArrayList<>(); for (int i = 0; i < observableCount; i++) { SubscriptionCheckObservable sco = new SubscriptionCheckObservable(subscriptionCount, maxConcurrent); scos.add(sco); @@ -74,7 +74,7 @@ public void maxConcurrent() { } Iterator iter = Observable.merge(os, maxConcurrent).blockingIterable().iterator(); - List actual = new ArrayList(); + List actual = new ArrayList<>(); while (iter.hasNext()) { actual.add(iter.next()); } @@ -99,7 +99,7 @@ private static class SubscriptionCheckObservable implements ObservableSource t1) { - t1.onSubscribe(Disposables.empty()); + t1.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override @@ -126,7 +126,7 @@ public void run() { @Test public void mergeALotOfSourcesOneByOneSynchronously() { int n = 10000; - List> sourceList = new ArrayList>(n); + List> sourceList = new ArrayList<>(n); for (int i = 0; i < n; i++) { sourceList.add(Observable.just(i)); } @@ -142,7 +142,7 @@ public void mergeALotOfSourcesOneByOneSynchronously() { @Test public void mergeALotOfSourcesOneByOneSynchronouslyTakeHalf() { int n = 10000; - List> sourceList = new ArrayList>(n); + List> sourceList = new ArrayList<>(n); for (int i = 0; i < n; i++) { sourceList.add(Observable.just(i)); } @@ -158,9 +158,9 @@ public void mergeALotOfSourcesOneByOneSynchronouslyTakeHalf() { @Test public void simple() { for (int i = 1; i < 100; i++) { - TestObserverEx to = new TestObserverEx(); - List> sourceList = new ArrayList>(i); - List result = new ArrayList(i); + TestObserverEx to = new TestObserverEx<>(); + List> sourceList = new ArrayList<>(i); + List result = new ArrayList<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Observable.just(j)); result.add(j); @@ -177,9 +177,9 @@ public void simple() { @Test public void simpleOneLess() { for (int i = 2; i < 100; i++) { - TestObserverEx to = new TestObserverEx(); - List> sourceList = new ArrayList>(i); - List result = new ArrayList(i); + TestObserverEx to = new TestObserverEx<>(); + List> sourceList = new ArrayList<>(i); + List result = new ArrayList<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Observable.just(j)); result.add(j); @@ -209,9 +209,9 @@ public void simpleAsyncLoop() { @Test public void simpleAsync() { for (int i = 1; i < 50; i++) { - TestObserver to = new TestObserver(); - List> sourceList = new ArrayList>(i); - Set expected = new HashSet(i); + TestObserver to = new TestObserver<>(); + List> sourceList = new ArrayList<>(i); + Set expected = new HashSet<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Observable.just(j).subscribeOn(Schedulers.io())); expected.add(j); @@ -221,7 +221,7 @@ public void simpleAsync() { to.awaitDone(1, TimeUnit.SECONDS); to.assertNoErrors(); - Set actual = new HashSet(to.values()); + Set actual = new HashSet<>(to.values()); assertEquals(expected, actual); } @@ -241,9 +241,9 @@ public void simpleOneLessAsync() { if (System.currentTimeMillis() - t > TimeUnit.SECONDS.toMillis(9)) { break; } - TestObserver to = new TestObserver(); - List> sourceList = new ArrayList>(i); - Set expected = new HashSet(i); + TestObserver to = new TestObserver<>(); + List> sourceList = new ArrayList<>(i); + Set expected = new HashSet<>(i); for (int j = 1; j <= i; j++) { sourceList.add(Observable.just(j).subscribeOn(Schedulers.io())); expected.add(j); @@ -253,7 +253,7 @@ public void simpleOneLessAsync() { to.awaitDone(1, TimeUnit.SECONDS); to.assertNoErrors(); - Set actual = new HashSet(to.values()); + Set actual = new HashSet<>(to.values()); assertEquals(expected, actual); } @@ -261,13 +261,13 @@ public void simpleOneLessAsync() { @Test public void take() throws Exception { - List> sourceList = new ArrayList>(3); + List> sourceList = new ArrayList<>(3); sourceList.add(Observable.range(0, 100000).subscribeOn(Schedulers.io())); sourceList.add(Observable.range(0, 100000).subscribeOn(Schedulers.io())); sourceList.add(Observable.range(0, 100000).subscribeOn(Schedulers.io())); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.merge(sourceList, 2).take(5).subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeTest.java index 8736bd4ae3..02ddf09fd0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -79,7 +79,7 @@ public void mergeObservableOfObservables() { @Override public void subscribe(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); // simulate what would happen in an Observable observer.onNext(o1); observer.onNext(o2); @@ -112,7 +112,7 @@ public void mergeArray() { public void mergeList() { final Observable o1 = Observable.unsafeCreate(new TestSynchronousObservable()); final Observable o2 = Observable.unsafeCreate(new TestSynchronousObservable()); - List> listOfObservables = new ArrayList>(); + List> listOfObservables = new ArrayList<>(); listOfObservables.add(o1); listOfObservables.add(o2); @@ -135,7 +135,7 @@ public void unSubscribeObservableOfObservables() throws InterruptedException { @Override public void subscribe(final Observer> observer) { // verbose on purpose so I can track the inside of it - final Disposable upstream = Disposables.fromRunnable(new Runnable() { + final Disposable upstream = Disposable.fromRunnable(new Runnable() { @Override public void run() { System.out.println("*** unsubscribed"); @@ -191,7 +191,7 @@ public void mergeArrayWithThreading() { final TestASynchronousObservable o2 = new TestASynchronousObservable(); Observable m = Observable.merge(Observable.unsafeCreate(o1), Observable.unsafeCreate(o2)); - TestObserver to = new TestObserver(stringObserver); + TestObserver to = new TestObserver<>(stringObserver); m.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -342,7 +342,7 @@ private static class TestSynchronousObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("hello"); observer.onComplete(); } @@ -354,7 +354,7 @@ private static class TestASynchronousObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); t = new Thread(new Runnable() { @Override @@ -385,7 +385,7 @@ private static class TestErrorObservable implements ObservableSource { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); for (String s : valuesToReturn) { if (s == null) { System.out.println("throwing exception"); @@ -408,7 +408,7 @@ public void unsubscribeAsObservablesComplete() { AtomicBoolean os2 = new AtomicBoolean(false); Observable o2 = createObservableOf5IntervalsOf1SecondIncrementsWithSubscriptionHook(scheduler2, os2); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.merge(o1, o2).subscribe(to); // we haven't incremented time so nothing should be received yet @@ -450,7 +450,7 @@ public void earlyUnsubscribe() { AtomicBoolean os2 = new AtomicBoolean(false); Observable o2 = createObservableOf5IntervalsOf1SecondIncrementsWithSubscriptionHook(scheduler2, os2); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.merge(o1, o2).subscribe(to); // we haven't incremented time so nothing should be received yet @@ -486,7 +486,7 @@ public void subscribe(final Observer child) { .subscribe(new Observer() { @Override public void onSubscribe(final Disposable d) { - child.onSubscribe(Disposables.fromRunnable(new Runnable() { + child.onSubscribe(Disposable.fromRunnable(new Runnable() { @Override public void run() { unsubscribed.set(true); @@ -523,7 +523,7 @@ public void concurrency() { for (int i = 0; i < 10; i++) { Observable merge = Observable.merge(o, o, o); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); merge.subscribe(to); to.awaitDone(3, TimeUnit.SECONDS); @@ -545,7 +545,7 @@ public void concurrencyWithSleeping() { public void subscribe(final Observer observer) { Worker inner = Schedulers.newThread().createWorker(); final CompositeDisposable as = new CompositeDisposable(); - as.add(Disposables.empty()); + as.add(Disposable.empty()); as.add(inner); observer.onSubscribe(as); @@ -576,7 +576,7 @@ public void run() { for (int i = 0; i < 10; i++) { Observable merge = Observable.merge(o, o, o); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); merge.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -595,7 +595,7 @@ public void concurrencyWithBrokenOnCompleteContract() { public void subscribe(final Observer observer) { Worker inner = Schedulers.newThread().createWorker(); final CompositeDisposable as = new CompositeDisposable(); - as.add(Disposables.empty()); + as.add(Disposable.empty()); as.add(inner); observer.onSubscribe(as); @@ -623,7 +623,7 @@ public void run() { for (int i = 0; i < 10; i++) { Observable merge = Observable.merge(o, o, o); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); merge.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -806,7 +806,7 @@ public void onNext(Integer t) { @Test public void merge1AsyncStreamOf1() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNAsyncStreamsOfN(1, 1).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -815,7 +815,7 @@ public void merge1AsyncStreamOf1() { @Test public void merge1AsyncStreamOf1000() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNAsyncStreamsOfN(1, 1000).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -824,7 +824,7 @@ public void merge1AsyncStreamOf1000() { @Test public void merge10AsyncStreamOf1000() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNAsyncStreamsOfN(10, 1000).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -833,7 +833,7 @@ public void merge10AsyncStreamOf1000() { @Test public void merge1000AsyncStreamOf1000() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNAsyncStreamsOfN(1000, 1000).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -842,7 +842,7 @@ public void merge1000AsyncStreamOf1000() { @Test public void merge2000AsyncStreamOf100() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNAsyncStreamsOfN(2000, 100).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -851,7 +851,7 @@ public void merge2000AsyncStreamOf100() { @Test public void merge100AsyncStreamOf1() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNAsyncStreamsOfN(100, 1).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -873,7 +873,7 @@ public Observable apply(Integer i) { @Test public void merge1SyncStreamOf1() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNSyncStreamsOfN(1, 1).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -882,7 +882,7 @@ public void merge1SyncStreamOf1() { @Test public void merge1SyncStreamOf1000000() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNSyncStreamsOfN(1, 1000000).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -891,7 +891,7 @@ public void merge1SyncStreamOf1000000() { @Test public void merge1000SyncStreamOf1000() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNSyncStreamsOfN(1000, 1000).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -900,7 +900,7 @@ public void merge1000SyncStreamOf1000() { @Test public void merge10000SyncStreamOf10() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNSyncStreamsOfN(10000, 10).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -909,7 +909,7 @@ public void merge10000SyncStreamOf10() { @Test public void merge1000000SyncStreamOf1() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); mergeNSyncStreamsOfN(1000000, 1).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -956,7 +956,7 @@ public boolean hasNext() { @Test public void mergeManyAsyncSingle() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable> os = Observable.range(1, 10000) .map(new Function>() { @@ -966,7 +966,7 @@ public Observable apply(final Integer i) { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); if (i < 500) { try { Thread.sleep(1); @@ -1004,7 +1004,7 @@ public Observable apply(Integer t) { ; void runMerge(Function> func, TestObserverEx to) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(i); } @@ -1022,12 +1022,12 @@ void runMerge(Function> func, TestObserverEx()); + runMerge(toScalar, new TestObserverEx<>()); } @Test public void fastMergeHiddenScalar() { - runMerge(toHiddenScalar, new TestObserverEx()); + runMerge(toHiddenScalar, new TestObserverEx<>()); } @Test @@ -1065,7 +1065,6 @@ public void onNext(Integer t) { } } - @SuppressWarnings("unchecked") @Test public void mergeArray2() { Observable.mergeArray(Observable.just(1), Observable.just(2)) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletableTest.java index 8cdb78df76..4b9ee5a9d2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public class ObservableMergeWithCompletableTest extends RxJavaTest { @Test public void normal() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5).mergeWith( Completable.fromAction(new Action() { @@ -46,7 +46,7 @@ public void run() throws Exception { @Test public void take() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.range(1, 5).mergeWith( Completable.complete() @@ -123,7 +123,7 @@ public void isDisposed() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybeTest.java index 5fd7763825..3140c5c118 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -178,11 +178,11 @@ public void onNext(Integer t) { public void onErrorMainOverflow() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> observerRef = new AtomicReference>(); + final AtomicReference> observerRef = new AtomicReference<>(); TestObserver to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observerRef.set(observer); } } @@ -234,7 +234,7 @@ public void isDisposed() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingleTest.java index a4e38c0936..9d6171ba06 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableMergeWithSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -170,11 +170,11 @@ public void onNext(Integer t) { public void onErrorMainOverflow() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> observerRef = new AtomicReference>(); + final AtomicReference> observerRef = new AtomicReference<>(); TestObserver to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observerRef.set(observer); } } @@ -226,7 +226,7 @@ public void isDisposed() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOnTest.java index 0602249ceb..5f41470db0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableObserveOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,11 +31,13 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.flowable.FlowableObserveOnTest.DisposeTrackingScheduler; import io.reactivex.rxjava3.internal.operators.observable.ObservableObserveOn.ObserveOnObserver; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.observers.*; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.SimpleQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.*; @@ -66,7 +68,7 @@ public void ordering() throws InterruptedException { Observer observer = TestHelper.mockObserver(); InOrder inOrder = inOrder(observer); - TestObserverEx to = new TestObserverEx(observer); + TestObserverEx to = new TestObserverEx<>(observer); obs.observeOn(Schedulers.computation()).subscribe(to); @@ -387,7 +389,7 @@ public void afterUnsubscribeCalledThenObserverOnNextNeverCalled() { final TestScheduler testScheduler = new TestScheduler(); final Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); Observable.just(1, 2, 3) .observeOn(testScheduler) @@ -428,7 +430,7 @@ public boolean hasNext() { } }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o .take(7) .observeOn(Schedulers.newThread()) @@ -441,7 +443,7 @@ public boolean hasNext() { @Test public void asyncChild() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(0, 100000).observeOn(Schedulers.newThread()).observeOn(Schedulers.newThread()).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -495,7 +497,7 @@ public void badSource() { TestObserver to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onNext(1); observer.onError(new TestException()); @@ -565,7 +567,7 @@ public void inputAsyncFusedErrorDelayed() { @Test public void outputFused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.range(1, 5).hide() .observeOn(Schedulers.single()) @@ -578,7 +580,7 @@ public void outputFused() { @Test public void outputFusedReject() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Observable.range(1, 5).hide() .observeOn(Schedulers.single()) @@ -591,7 +593,7 @@ public void outputFusedReject() { @Test public void inputOutputAsyncFusedError() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); UnicastSubject us = UnicastSubject.create(); @@ -611,7 +613,7 @@ public void inputOutputAsyncFusedError() { @Test public void inputOutputAsyncFusedErrorDelayed() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); UnicastSubject us = UnicastSubject.create(); @@ -675,7 +677,7 @@ public void nonFusedPollThrows() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); @SuppressWarnings("unchecked") ObserveOnObserver oo = (ObserveOnObserver)observer; @@ -775,12 +777,12 @@ public void workerNotDisposedPrematurelySyncInNormalOut() { public void workerNotDisposedPrematurelyAsyncInNormalOut() { DisposeTrackingScheduler s = new DisposeTrackingScheduler(); - UnicastSubject up = UnicastSubject.create(); - up.onNext(1); - up.onComplete(); + UnicastSubject us = UnicastSubject.create(); + us.onNext(1); + us.onComplete(); Observable.concat( - up.observeOn(s), + us.observeOn(s), Observable.just(2) ) .test() diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorCompleteTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorCompleteTest.java new file mode 100644 index 0000000000..a4671dd535 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorCompleteTest.java @@ -0,0 +1,134 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.junit.Assert.*; + +import java.io.IOException; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.testsupport.*; + +public class ObservableOnErrorCompleteTest { + + @Test + public void normal() { + Observable.range(1, 10) + .onErrorComplete() + .test() + .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + } + + @Test + public void empty() { + Observable.empty() + .onErrorComplete() + .test() + .assertResult(); + } + + @Test + public void error() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable.error(new TestException()) + .onErrorComplete() + .test() + .assertResult(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorMatches() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable.error(new TestException()) + .onErrorComplete(error -> error instanceof TestException) + .test() + .assertResult(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorNotMatches() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable.error(new IOException()) + .onErrorComplete(error -> error instanceof TestException) + .test() + .assertFailure(IOException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorPredicateCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestObserverEx to = Observable.error(new IOException()) + .onErrorComplete(error -> { throw new TestException(); }) + .subscribeWith(new TestObserverEx<>()) + .assertFailure(CompositeException.class); + + TestHelper.assertError(to, 0, IOException.class); + TestHelper.assertError(to, 1, TestException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void itemsThenError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable.range(1, 5) + .map(v -> 4 / (3 - v)) + .onErrorComplete() + .test() + .assertResult(2, 4); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void dispose() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps + .onErrorComplete() + .test(); + + assertTrue("No subscribers?!", ps.hasObservers()); + + to.dispose(); + + assertFalse("Still subscribers?!", ps.hasObservers()); + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(f -> f.onErrorComplete()); + } + + @Test + public void isDisposed() { + TestHelper.checkDisposed(PublishSubject.create().onErrorComplete()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeNextTest.java index 2761a6c14b..ed3e8dc571 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeNextTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeNextTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,12 +21,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.mockito.Mockito; import org.reactivestreams.Subscription; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; @@ -37,12 +37,12 @@ public class ObservableOnErrorResumeNextTest extends RxJavaTest { @Test public void resumeNextWithSynchronousExecution() { - final AtomicReference receivedException = new AtomicReference(); + final AtomicReference receivedException = new AtomicReference<>(); Observable w = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("one"); observer.onError(new Throwable("injected failure")); observer.onNext("two"); @@ -77,7 +77,7 @@ public Observable apply(Throwable t1) { @Test public void resumeNextWithAsyncExecution() { - final AtomicReference receivedException = new AtomicReference(); + final AtomicReference receivedException = new AtomicReference<>(); Subscription s = mock(Subscription.class); TestObservable w = new TestObservable(s, "one"); Function> resume = new Function>() { @@ -174,7 +174,7 @@ public Observable apply(Throwable t1) { Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); o.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -199,7 +199,7 @@ static class TestObservable implements ObservableSource { @Override public void subscribe(final Observer observer) { System.out.println("TestObservable subscribed to ..."); - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); t = new Thread(new Runnable() { @Override @@ -226,7 +226,7 @@ public void run() { @Test public void backpressure() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(0, 100000) .onErrorResumeNext(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeWithTest.java index 217335ec08..7b659c9444 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeWithTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorResumeWithTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -146,7 +146,7 @@ public void run() { @Test public void backpressure() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(0, 100000) .onErrorResumeWith(Observable.just(1)) .observeOn(Schedulers.computation()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturnTest.java index 3f20a8a7db..da7acf2b47 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableOnErrorReturnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,11 +20,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.mockito.Mockito; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.observers.TestObserver; @@ -37,7 +37,7 @@ public class ObservableOnErrorReturnTest extends RxJavaTest { public void resumeNext() { TestObservable f = new TestObservable("one"); Observable w = Observable.unsafeCreate(f); - final AtomicReference capturedException = new AtomicReference(); + final AtomicReference capturedException = new AtomicReference<>(); Observable observable = w.onErrorReturn(new Function() { @@ -72,7 +72,7 @@ public String apply(Throwable e) { public void functionThrowsError() { TestObservable f = new TestObservable("one"); Observable w = Observable.unsafeCreate(f); - final AtomicReference capturedException = new AtomicReference(); + final AtomicReference capturedException = new AtomicReference<>(); Observable observable = w.onErrorReturn(new Function() { @@ -130,7 +130,7 @@ public String apply(Throwable t1) { }); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); observable.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -144,7 +144,7 @@ public String apply(Throwable t1) { @Test public void backpressure() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(0, 100000) .onErrorReturn(new Function() { @@ -188,7 +188,7 @@ private static class TestObservable implements ObservableSource { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); System.out.println("TestObservable subscribed to ..."); t = new Thread(new Runnable() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java index 38f0140e5d..edec6a517d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservablePublishTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ public void publish() throws InterruptedException { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override @@ -126,7 +126,7 @@ public void run() { }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.merge(fast, slow).subscribe(to); is.connect(); to.awaitDone(5, TimeUnit.SECONDS); @@ -146,7 +146,7 @@ public void accept(Integer t1) { } }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); xs.publish(new Function, Observable>() { @Override @@ -173,7 +173,7 @@ public boolean test(Integer i) { @Test public void takeUntilWithPublishedStream() { Observable xs = Observable.range(0, Flowable.bufferSize() * 2); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); ConnectableObservable xsp = xs.publish(); xsp.takeUntil(xsp.skipWhile(new Predicate() { @@ -209,7 +209,7 @@ public void run() { final AtomicBoolean child1Unsubscribed = new AtomicBoolean(); final AtomicBoolean child2Unsubscribed = new AtomicBoolean(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); final TestObserver to1 = new TestObserver() { @Override @@ -257,7 +257,7 @@ public void connectWithNoSubscriber() { co.connect(); // Emit 0 scheduler.advanceTimeBy(15, TimeUnit.MILLISECONDS); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); co.subscribe(to); // Emit 1 and 2 scheduler.advanceTimeBy(50, TimeUnit.MILLISECONDS); @@ -270,7 +270,7 @@ public void connectWithNoSubscriber() { public void subscribeAfterDisconnectThenConnect() { ConnectableObservable source = Observable.just(1).publish(); - TestObserverEx to1 = new TestObserverEx(); + TestObserverEx to1 = new TestObserverEx<>(); source.subscribe(to1); @@ -282,7 +282,7 @@ public void subscribeAfterDisconnectThenConnect() { source.reset(); - TestObserverEx to2 = new TestObserverEx(); + TestObserverEx to2 = new TestObserverEx<>(); source.subscribe(to2); @@ -300,7 +300,7 @@ public void subscribeAfterDisconnectThenConnect() { public void noSubscriberRetentionOnCompleted() { ObservablePublish source = (ObservablePublish)Observable.just(1).publish(); - TestObserverEx to1 = new TestObserverEx(); + TestObserverEx to1 = new TestObserverEx<>(); source.subscribe(to1); @@ -354,7 +354,7 @@ public void connectIsIdempotent() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer t) { - t.onSubscribe(Disposables.empty()); + t.onSubscribe(Disposable.empty()); calls.getAndIncrement(); } }); @@ -382,9 +382,9 @@ public void observeOn() { Observable obs = co.observeOn(Schedulers.computation()); for (int i = 0; i < 1000; i++) { for (int j = 1; j < 6; j++) { - List> tos = new ArrayList>(); + List> tos = new ArrayList<>(); for (int k = 1; k < j; k++) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); tos.add(to); obs.subscribe(to); } @@ -480,7 +480,7 @@ public void addRemoveRace() { final TestObserver to = co.test(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -589,7 +589,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); observer.onNext(2); @@ -628,7 +628,7 @@ public void subscribeDisconnectRace() { final ConnectableObservable co = ps.publish(); final Disposable d = co.connect(); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -715,7 +715,7 @@ protected void subscribeActual(Observer observer) { .connect() .dispose(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); sub[0].onSubscribe(bs); @@ -762,7 +762,7 @@ public void disposedUpfront() { @Test public void altConnectCrash() { try { - new ObservablePublish(Observable.empty()) + new ObservablePublish<>(Observable.empty()) .connect(new Consumer() { @Override public void accept(Disposable t) throws Exception { @@ -779,7 +779,7 @@ public void accept(Disposable t) throws Exception { public void altConnectRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { final ConnectableObservable co = - new ObservablePublish(Observable.never()); + new ObservablePublish<>(Observable.never()); Runnable r = new Runnable() { @Override @@ -866,4 +866,31 @@ public void disposeResets() { to.assertValuesOnly(1); } + + @Test + public void disposeNoNeedForReset() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.publish(); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLongTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLongTest.java index e25cb17b8a..c19219cdea 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLongTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeLongTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,8 +24,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.*; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.*; public class ObservableRangeLongTest extends RxJavaTest { @@ -93,14 +93,14 @@ public void rangeWithOverflow5() { @Test public void noBackpressure() { - ArrayList list = new ArrayList(Flowable.bufferSize() * 2); + ArrayList list = new ArrayList<>(Flowable.bufferSize() * 2); for (long i = 1; i <= Flowable.bufferSize() * 2 + 1; i++) { list.add(i); } Observable o = Observable.rangeLong(1, list.size()); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); o.subscribe(to); @@ -137,7 +137,7 @@ public void onNext(Long t) { @Test public void nearMaxValueWithoutBackpressure() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.rangeLong(Long.MAX_VALUE - 1L, 2L).subscribe(to); to.assertComplete(); @@ -171,7 +171,7 @@ public void noOverflow() { @Test public void fused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Observable.rangeLong(1, 2).subscribe(to); @@ -181,7 +181,7 @@ public void fused() { @Test public void fusedReject() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC); Observable.rangeLong(1, 2).subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeTest.java index 2f9374ad5a..0f461a7154 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRangeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,8 +24,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.*; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.*; public class ObservableRangeTest extends RxJavaTest { @@ -94,14 +94,14 @@ public void rangeWithOverflow5() { @Test public void noBackpressure() { - ArrayList list = new ArrayList(Flowable.bufferSize() * 2); + ArrayList list = new ArrayList<>(Flowable.bufferSize() * 2); for (int i = 1; i <= Flowable.bufferSize() * 2 + 1; i++) { list.add(i); } Observable o = Observable.range(1, list.size()); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); o.subscribe(to); @@ -138,7 +138,7 @@ public void onNext(Integer t) { @Test public void nearMaxValueWithoutBackpressure() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(Integer.MAX_VALUE - 1, 2).subscribe(to); to.assertComplete(); @@ -158,7 +158,7 @@ public void negativeCount() { @Test public void requestWrongFusion() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ASYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ASYNC); Observable.range(1, 5) .subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRedoTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRedoTest.java index cac0b75d1a..adb2caf4a3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRedoTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRedoTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ public class ObservableRedoTest extends RxJavaTest { @Test public void redoCancel() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.just(1) .repeatWhen(new Function, ObservableSource>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceTest.java index f05e007068..0feab233d3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReduceTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,10 +19,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -291,7 +291,7 @@ public void reduceMaybeBadSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onNext(1); observer.onError(new TestException()); @@ -345,7 +345,7 @@ public void seedBadSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onNext(1); observer.onError(new TestException()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java index d5bcf0440e..638f694a88 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRefCountTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,25 @@ public class ObservableRefCountTest extends RxJavaTest { @Test - public void refCountAsync() { + public void refCountAsync() throws InterruptedException { + // Flaky + for (int i = 0; i < 10; i++) { + try { + refCountAsyncActual(); + return; + } catch (AssertionError ex) { + if (i == 9) { + throw ex; + } + Thread.sleep((int)(200 * (Math.random() * 10 + 1))); + } + } + } + + /** + * Tries to coordinate async counting but it is flaky due to the low 10s of milliseconds. + */ + void refCountAsyncActual() { final AtomicInteger subscribeCount = new AtomicInteger(); final AtomicInteger nextCount = new AtomicInteger(); Observable r = Observable.interval(0, 25, TimeUnit.MILLISECONDS) @@ -190,8 +208,8 @@ public void run() { .publish().refCount(); for (int i = 0; i < 10; i++) { - TestObserver to1 = new TestObserver(); - TestObserver to2 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); + TestObserver to2 = new TestObserver<>(); r.subscribe(to1); r.subscribe(to2); try { @@ -233,7 +251,7 @@ public void run() { } }); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); o.publish().refCount().subscribeOn(Schedulers.newThread()).subscribe(observer); System.out.println("send unsubscribe"); // wait until connected @@ -278,7 +296,7 @@ public void accept(Disposable d) { } }); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); o.publish().refCount().subscribeOn(Schedulers.computation()).subscribe(observer); System.out.println("send unsubscribe"); @@ -310,7 +328,7 @@ private Observable synchronousInterval() { @Override public void subscribe(Observer observer) { final AtomicBoolean cancel = new AtomicBoolean(); - observer.onSubscribe(Disposables.fromRunnable(new Runnable() { + observer.onSubscribe(Disposable.fromRunnable(new Runnable() { @Override public void run() { cancel.set(true); @@ -338,7 +356,7 @@ public void onlyFirstShouldSubscribeAndLastUnsubscribe() { @Override public void subscribe(Observer observer) { subscriptionCount.incrementAndGet(); - observer.onSubscribe(Disposables.fromRunnable(new Runnable() { + observer.onSubscribe(Disposable.fromRunnable(new Runnable() { @Override public void run() { unsubscriptionCount.incrementAndGet(); @@ -367,7 +385,7 @@ public void refCount() { Observable interval = Observable.interval(100, TimeUnit.MILLISECONDS, s).publish().refCount(); // subscribe list1 - final List list1 = new ArrayList(); + final List list1 = new ArrayList<>(); Disposable d1 = interval.subscribe(new Consumer() { @Override public void accept(Long t1) { @@ -382,7 +400,7 @@ public void accept(Long t1) { assertEquals(1L, list1.get(1).longValue()); // subscribe list2 - final List list2 = new ArrayList(); + final List list2 = new ArrayList<>(); Disposable d2 = interval.subscribe(new Consumer() { @Override public void accept(Long t1) { @@ -427,7 +445,7 @@ public void accept(Long t1) { // subscribing a new one should start over because the source should have been unsubscribed // subscribe list3 - final List list3 = new ArrayList(); + final List list3 = new ArrayList<>(); interval.subscribe(new Consumer() { @Override public void accept(Long t1) { @@ -498,8 +516,8 @@ public Integer apply(Integer t1, Integer t2) { }) .publish().refCount(); - TestObserverEx to1 = new TestObserverEx(); - TestObserverEx to2 = new TestObserverEx(); + TestObserverEx to1 = new TestObserverEx<>(); + TestObserverEx to2 = new TestObserverEx<>(); combined.subscribe(to1); combined.subscribe(to2); @@ -623,7 +641,7 @@ public void reset() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.disposed()); + observer.onSubscribe(Disposable.disposed()); } }.refCount(); @@ -779,7 +797,7 @@ static final class BadObservableSubscribe extends ConnectableObservable @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -806,7 +824,7 @@ public void reset() { @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -814,7 +832,7 @@ public void connect(Consumer connection) { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); } } @@ -832,11 +850,12 @@ public void reset() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); } } @Test + @SuppressUndeliverable public void badSourceSubscribe() { BadObservableSubscribe bo = new BadObservableSubscribe(); @@ -863,6 +882,7 @@ public void badSourceDispose() { } @Test + @SuppressUndeliverable public void badSourceConnect() { BadObservableConnect bo = new BadObservableConnect(); @@ -882,7 +902,7 @@ static final class BadObservableSubscribe2 extends ConnectableObservable @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -896,7 +916,7 @@ public void reset() { @Override protected void subscribeActual(Observer observer) { if (++count == 1) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); } else { throw new TestException("subscribeActual"); } @@ -904,6 +924,7 @@ protected void subscribeActual(Observer observer) { } @Test + @SuppressUndeliverable public void badSourceSubscribe2() { BadObservableSubscribe2 bo = new BadObservableSubscribe2(); @@ -922,7 +943,7 @@ static final class BadObservableConnect2 extends ConnectableObservable { @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -935,12 +956,13 @@ public void reset() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); } } @Test + @SuppressUndeliverable public void badSourceCompleteDisconnect() { BadObservableConnect2 bo = new BadObservableConnect2(); @@ -1133,7 +1155,7 @@ public void unsubscribeSubscribeRace() { final TestObserver to1 = source.test(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -1163,7 +1185,7 @@ static final class BadObservableDoubleOnX extends ConnectableObservable @Override public void connect(Consumer connection) { try { - connection.accept(Disposables.empty()); + connection.accept(Disposable.empty()); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -1176,8 +1198,8 @@ public void reset() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onComplete(); observer.onError(new TestException()); @@ -1337,11 +1359,11 @@ protected void subscribeActual(Observer observer) { @Test public void timeoutResetsSource() { - TestConnectableObservable tco = new TestConnectableObservable(); + TestConnectableObservable tco = new TestConnectableObservable<>(); ObservableRefCount o = (ObservableRefCount)tco.refCount(); RefConnection rc = new RefConnection(o); - rc.set(Disposables.empty()); + rc.set(Disposable.empty()); o.connection = rc; o.timeout(rc); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatTest.java index 55906d1214..8f3287e7e0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRepeatTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -74,7 +74,7 @@ public void repeatTakeWithSubscribeOn() throws InterruptedException { @Override public void subscribe(Observer sub) { - sub.onSubscribe(Disposables.empty()); + sub.onSubscribe(Disposable.empty()); counter.incrementAndGet(); sub.onNext(1); sub.onNext(2); @@ -164,7 +164,7 @@ public void repeatAndDistinctUnbounded() { .repeat(3) .distinct(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); src.subscribe(to); @@ -176,8 +176,8 @@ public void repeatAndDistinctUnbounded() { /** Issue #2844: wrong target of request. */ @Test public void repeatRetarget() { - final List concatBase = new ArrayList(); - TestObserver to = new TestObserver(); + final List concatBase = new ArrayList<>(); + TestObserver to = new TestObserver<>(); Observable.just(1, 2) .repeat(5) .concatMap(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java index e1c0bf6f8c..aa1bcb4359 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayEagerTruncateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -729,7 +729,7 @@ void truncate() { buf.addLast(new Node(4)); buf.addLast(new Node(5)); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.collect(values); Assert.assertEquals(Arrays.asList(1, 2, 3, 4, 5), values); @@ -753,8 +753,8 @@ void truncate() { @Test public void timedAndSizedTruncation() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, false); - List values = new ArrayList(); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, false); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -792,12 +792,12 @@ public void timedAndSizedTruncation() { @Test public void timedAndSizedTruncationError() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, false); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, false); Assert.assertFalse(buf.hasCompleted()); Assert.assertFalse(buf.hasError()); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -835,8 +835,8 @@ public void timedAndSizedTruncationError() { @Test public void sizedTruncation() { - SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer(2, false); - List values = new ArrayList(); + SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer<>(2, false); + List values = new ArrayList<>(); buf.next(1); buf.next(2); @@ -871,7 +871,7 @@ public void sizedTruncation() { public void coldReplayNoBackpressure() { Observable source = Observable.range(0, 1000).replay().autoConnect(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.subscribe(to); @@ -892,7 +892,7 @@ public void cache() throws InterruptedException { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override @@ -949,7 +949,7 @@ public void unsubscribeSource() throws Throwable { @Test public void take() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable cached = Observable.range(1, 100).replay().autoConnect(); cached.take(10).subscribe(to); @@ -965,7 +965,7 @@ public void take() { public void async() { Observable source = Observable.range(1, 10000); for (int i = 0; i < 100; i++) { - TestObserverEx to1 = new TestObserverEx(); + TestObserverEx to1 = new TestObserverEx<>(); Observable cached = source.replay().autoConnect(); @@ -976,7 +976,7 @@ public void async() { to1.assertTerminated(); assertEquals(10000, to1.values().size()); - TestObserverEx to2 = new TestObserverEx(); + TestObserverEx to2 = new TestObserverEx<>(); cached.observeOn(Schedulers.computation()).subscribe(to2); to2.awaitDone(2, TimeUnit.SECONDS); @@ -995,14 +995,14 @@ public void asyncComeAndGo() { Observable output = cached.observeOn(Schedulers.computation()); - List> list = new ArrayList>(100); + List> list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); list.add(to); output.skip(i * 10).take(10).subscribe(to); } - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 10; i++) { expected.add((long)(i - 10)); } @@ -1028,7 +1028,7 @@ public void noMissingBackpressureException() { Observable firehose = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer t) { - t.onSubscribe(Disposables.empty()); + t.onSubscribe(Disposable.empty()); for (int i = 0; i < m; i++) { t.onNext(i); } @@ -1036,7 +1036,7 @@ public void subscribe(Observer t) { } }); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); firehose.replay().autoConnect().observeOn(Schedulers.computation()).takeLast(100).subscribe(to); to.awaitDone(3, TimeUnit.SECONDS); @@ -1052,14 +1052,14 @@ public void valuesAndThenError() { .concatWith(Observable.error(new TestException())) .replay().autoConnect(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.subscribe(to); to.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); to.assertNotComplete(); Assert.assertEquals(1, to.errors().size()); - TestObserverEx to2 = new TestObserverEx(); + TestObserverEx to2 = new TestObserverEx<>(); source.subscribe(to2); to2.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -1130,8 +1130,8 @@ public void subscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableObservable co = Observable.range(1, 3).replay(); - final TestObserver to1 = new TestObserver(); - final TestObserver to2 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); + final TestObserver to2 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -1156,8 +1156,8 @@ public void addRemoveRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableObservable co = Observable.range(1, 3).replay(); - final TestObserver to1 = new TestObserver(); - final TestObserver to2 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); + final TestObserver to2 = new TestObserver<>(); co.subscribe(to1); @@ -1232,7 +1232,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onNext(1); observer.onError(new TestException("Second")); @@ -1256,7 +1256,7 @@ public void subscribeOnNextRace() { final ConnectableObservable co = ps.replay(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -1285,7 +1285,7 @@ public void unsubscribeOnNextRace() { final ConnectableObservable co = ps.replay(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); co.subscribe(to1); @@ -1314,7 +1314,7 @@ public void unsubscribeReplayRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableObservable co = Observable.range(1, 1000).replay(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); co.connect(); @@ -1438,7 +1438,7 @@ protected void subscribeActual(Observer observer) { .connect() .dispose(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); sub[0].onSubscribe(bs); @@ -1976,4 +1976,85 @@ public void timeAndSizeNoTerminalTruncationOnTimechange() { .assertComplete() .assertNoErrors(); } + + @Test + public void disposeNoNeedForResetSizeBound() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(10, true); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeBound() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } + + @Test + public void disposeNoNeedForResetTimeAndSIzeBound() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(10, 10, TimeUnit.MINUTES, Schedulers.single(), true); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java index 5ef40f340c..8b4ef97d02 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableReplayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -729,7 +729,7 @@ void truncate() { buf.addLast(new Node(4)); buf.addLast(new Node(5)); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.collect(values); Assert.assertEquals(Arrays.asList(1, 2, 3, 4, 5), values); @@ -753,8 +753,8 @@ void truncate() { @Test public void timedAndSizedTruncation() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, false); - List values = new ArrayList(); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, false); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -792,12 +792,12 @@ public void timedAndSizedTruncation() { @Test public void timedAndSizedTruncationError() { TestScheduler test = new TestScheduler(); - SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer(2, 2000, TimeUnit.MILLISECONDS, test, false); + SizeAndTimeBoundReplayBuffer buf = new SizeAndTimeBoundReplayBuffer<>(2, 2000, TimeUnit.MILLISECONDS, test, false); Assert.assertFalse(buf.hasCompleted()); Assert.assertFalse(buf.hasError()); - List values = new ArrayList(); + List values = new ArrayList<>(); buf.next(1); test.advanceTimeBy(1, TimeUnit.SECONDS); @@ -835,8 +835,8 @@ public void timedAndSizedTruncationError() { @Test public void sizedTruncation() { - SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer(2, false); - List values = new ArrayList(); + SizeBoundReplayBuffer buf = new SizeBoundReplayBuffer<>(2, false); + List values = new ArrayList<>(); buf.next(1); buf.next(2); @@ -871,7 +871,7 @@ public void sizedTruncation() { public void coldReplayNoBackpressure() { Observable source = Observable.range(0, 1000).replay().autoConnect(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.subscribe(to); @@ -892,7 +892,7 @@ public void cache() throws InterruptedException { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override @@ -949,7 +949,7 @@ public void unsubscribeSource() throws Throwable { @Test public void take() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable cached = Observable.range(1, 100).replay().autoConnect(); cached.take(10).subscribe(to); @@ -965,7 +965,7 @@ public void take() { public void async() { Observable source = Observable.range(1, 10000); for (int i = 0; i < 100; i++) { - TestObserverEx to1 = new TestObserverEx(); + TestObserverEx to1 = new TestObserverEx<>(); Observable cached = source.replay().autoConnect(); @@ -976,7 +976,7 @@ public void async() { to1.assertTerminated(); assertEquals(10000, to1.values().size()); - TestObserverEx to2 = new TestObserverEx(); + TestObserverEx to2 = new TestObserverEx<>(); cached.observeOn(Schedulers.computation()).subscribe(to2); to2.awaitDone(2, TimeUnit.SECONDS); @@ -995,14 +995,14 @@ public void asyncComeAndGo() { Observable output = cached.observeOn(Schedulers.computation()); - List> list = new ArrayList>(100); + List> list = new ArrayList<>(100); for (int i = 0; i < 100; i++) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); list.add(to); output.skip(i * 10).take(10).subscribe(to); } - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 10; i++) { expected.add((long)(i - 10)); } @@ -1028,7 +1028,7 @@ public void noMissingBackpressureException() { Observable firehose = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer t) { - t.onSubscribe(Disposables.empty()); + t.onSubscribe(Disposable.empty()); for (int i = 0; i < m; i++) { t.onNext(i); } @@ -1036,7 +1036,7 @@ public void subscribe(Observer t) { } }); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); firehose.replay().autoConnect().observeOn(Schedulers.computation()).takeLast(100).subscribe(to); to.awaitDone(3, TimeUnit.SECONDS); @@ -1052,14 +1052,14 @@ public void valuesAndThenError() { .concatWith(Observable.error(new TestException())) .replay().autoConnect(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.subscribe(to); to.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); to.assertNotComplete(); Assert.assertEquals(1, to.errors().size()); - TestObserverEx to2 = new TestObserverEx(); + TestObserverEx to2 = new TestObserverEx<>(); source.subscribe(to2); to2.assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); @@ -1130,8 +1130,8 @@ public void subscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableObservable co = Observable.range(1, 3).replay(); - final TestObserver to1 = new TestObserver(); - final TestObserver to2 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); + final TestObserver to2 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -1156,8 +1156,8 @@ public void addRemoveRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableObservable co = Observable.range(1, 3).replay(); - final TestObserver to1 = new TestObserver(); - final TestObserver to2 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); + final TestObserver to2 = new TestObserver<>(); co.subscribe(to1); @@ -1232,7 +1232,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onNext(1); observer.onError(new TestException("Second")); @@ -1256,7 +1256,7 @@ public void subscribeOnNextRace() { final ConnectableObservable co = ps.replay(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -1285,7 +1285,7 @@ public void unsubscribeOnNextRace() { final ConnectableObservable co = ps.replay(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); co.subscribe(to1); @@ -1314,7 +1314,7 @@ public void unsubscribeReplayRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final ConnectableObservable co = Observable.range(1, 1000).replay(); - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); co.connect(); @@ -1438,7 +1438,7 @@ protected void subscribeActual(Observer observer) { .connect() .dispose(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); sub[0].onSubscribe(bs); @@ -1698,4 +1698,57 @@ public void accept(byte[] v) throws Exception { Assert.fail("Bounded Replay Leak check: Memory leak detected: " + (initial / 1024.0 / 1024.0) + " -> " + after.get() / 1024.0 / 1024.0); } - }} + } + + @Test(expected = TestException.class) + public void connectDisposeCrash() { + ConnectableObservable co = Observable.never().replay(); + + co.connect(); + + co.connect(d -> { throw new TestException(); }); + } + + @Test + public void resetWhileNotConnectedIsNoOp() { + ConnectableObservable co = Observable.never().replay(); + + co.reset(); + } + + @Test + public void resetWhileActiveIsNoOp() { + ConnectableObservable co = Observable.never().replay(); + + co.connect(); + + co.reset(); + } + + @Test + public void disposeNoNeedForReset() { + PublishSubject ps = PublishSubject.create(); + + ConnectableObservable co = ps.replay(); + + TestObserver to = co.test(); + + Disposable d = co.connect(); + + ps.onNext(1); + + d.dispose(); + + to = co.test(); + + to.assertEmpty(); + + co.connect(); + + to.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(2); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableResourceWrapperTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableResourceWrapperTest.java index 9143762a5a..42d557e091 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableResourceWrapperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableResourceWrapperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,10 +27,10 @@ public class ObservableResourceWrapperTest extends RxJavaTest { @Test public void disposed() { - TestObserver to = new TestObserver(); - ObserverResourceWrapper orw = new ObserverResourceWrapper(to); + TestObserver to = new TestObserver<>(); + ObserverResourceWrapper orw = new ObserverResourceWrapper<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); orw.onSubscribe(d); @@ -43,19 +43,19 @@ public void disposed() { @Test public void doubleOnSubscribe() { - TestObserver to = new TestObserver(); - ObserverResourceWrapper orw = new ObserverResourceWrapper(to); + TestObserver to = new TestObserver<>(); + ObserverResourceWrapper orw = new ObserverResourceWrapper<>(to); TestHelper.doubleOnSubscribe(orw); } @Test public void onErrorDisposes() { - TestObserver to = new TestObserver(); - ObserverResourceWrapper orw = new ObserverResourceWrapper(to); + TestObserver to = new TestObserver<>(); + ObserverResourceWrapper orw = new ObserverResourceWrapper<>(to); - Disposable d = Disposables.empty(); - Disposable d1 = Disposables.empty(); + Disposable d = Disposable.empty(); + Disposable d1 = Disposable.empty(); orw.setResource(d1); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java index 26377c742c..13f19fb547 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -52,7 +52,7 @@ public void iterativeBackoff() { @Override public void subscribe(Observer t1) { - t1.onSubscribe(Disposables.empty()); + t1.onSubscribe(Disposable.empty()); System.out.println(count.get() + " @ " + String.valueOf(last - System.currentTimeMillis())); last = System.currentTimeMillis(); if (count.getAndDecrement() == 0) { @@ -64,7 +64,7 @@ public void subscribe(Observer t1) { } }); - TestObserver to = new TestObserver(consumer); + TestObserver to = new TestObserver<>(consumer); producer.retryWhen(new Function, Observable>() { @Override @@ -74,7 +74,7 @@ public Observable apply(Observable attempts) { .map(new Function() { @Override public Tuple apply(Throwable n) { - return new Tuple(new Long(1), n); + return new Tuple(1L, n); }}) .scan(new BiFunction() { @Override @@ -117,7 +117,7 @@ public void retryIndefinitely() { Observer observer = TestHelper.mockObserver(); int numRetries = 20; Observable origin = Observable.unsafeCreate(new FuncWithErrors(numRetries)); - origin.retry().subscribe(new TestObserver(observer)); + origin.retry().subscribe(new TestObserver<>(observer)); InOrder inOrder = inOrder(observer); // should show 3 attempts @@ -136,7 +136,7 @@ public void schedulingNotificationHandler() { Observer observer = TestHelper.mockObserver(); int numRetries = 2; Observable origin = Observable.unsafeCreate(new FuncWithErrors(numRetries)); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); origin.retryWhen(new Function, Observable>() { @Override public Observable apply(Observable t1) { @@ -205,7 +205,7 @@ public Integer apply(Throwable t1) { public void onCompletedFromNotificationHandler() { Observer observer = TestHelper.mockObserver(); Observable origin = Observable.unsafeCreate(new FuncWithErrors(1)); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); origin.retryWhen(new Function, Observable>() { @Override public Observable apply(Observable t1) { @@ -248,7 +248,7 @@ public void singleSubscriptionOnFirst() throws Exception { ObservableSource onSubscribe = new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); final int emit = inc.incrementAndGet(); observer.onNext(emit); observer.onComplete(); @@ -397,7 +397,7 @@ public static class FuncWithErrors implements ObservableSource { @Override public void subscribe(final Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); o.onNext("beginningEveryTime"); int i = count.getAndIncrement(); if (i < numFailures) { @@ -432,7 +432,7 @@ public void retryAllowsSubscriptionAfterAllSubscriptionsUnsubscribed() throws In @Override public void subscribe(Observer observer) { subsCount.incrementAndGet(); - observer.onSubscribe(Disposables.fromRunnable(new Runnable() { + observer.onSubscribe(Disposable.fromRunnable(new Runnable() { @Override public void run() { subsCount.decrementAndGet(); @@ -455,7 +455,7 @@ public void run() { public void sourceObservableCallsUnsubscribe() throws InterruptedException { final AtomicInteger subsCount = new AtomicInteger(0); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); ObservableSource onSubscribe = new ObservableSource() { @Override @@ -486,12 +486,12 @@ public void subscribe(Observer observer) { public void sourceObservableRetry1() throws InterruptedException { final AtomicInteger subsCount = new AtomicInteger(0); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); ObservableSource onSubscribe = new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); subsCount.incrementAndGet(); observer.onError(new RuntimeException("failed")); } @@ -505,12 +505,12 @@ public void subscribe(Observer observer) { public void sourceObservableRetry0() throws InterruptedException { final AtomicInteger subsCount = new AtomicInteger(0); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); ObservableSource onSubscribe = new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); subsCount.incrementAndGet(); observer.onError(new RuntimeException("failed")); } @@ -539,7 +539,7 @@ static final class SlowObservable implements ObservableSource { @Override public void subscribe(final Observer observer) { final AtomicBoolean terminate = new AtomicBoolean(false); - observer.onSubscribe(Disposables.fromRunnable(new Runnable() { + observer.onSubscribe(Disposable.fromRunnable(new Runnable() { @Override public void run() { terminate.set(true); @@ -572,7 +572,7 @@ public void run() { } } - /** Observer for listener on seperate thread. */ + /** Observer for listener on separate thread. */ static final class AsyncObserver extends DefaultObserver { protected CountDownLatch latch = new CountDownLatch(1); @@ -625,7 +625,7 @@ public void unsubscribeAfterError() { SlowObservable so = new SlowObservable(100, 0, "testUnsubscribeAfterError"); Observable o = Observable.unsafeCreate(so).retry(5); - AsyncObserver async = new AsyncObserver(observer); + AsyncObserver async = new AsyncObserver<>(observer); o.subscribe(async); @@ -649,7 +649,7 @@ public void timeoutWithRetry() { SlowObservable so = new SlowObservable(100, 10, "testTimeoutWithRetry"); Observable o = Observable.unsafeCreate(so).timeout(80, TimeUnit.MILLISECONDS).retry(5); - AsyncObserver async = new AsyncObserver(observer); + AsyncObserver async = new AsyncObserver<>(observer); o.subscribe(async); @@ -671,7 +671,7 @@ public void retryWithBackpressure() throws InterruptedException { for (int i = 0; i < 400; i++) { Observer observer = TestHelper.mockObserver(); Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); origin.retry().observeOn(Schedulers.computation()).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -702,7 +702,7 @@ public void retryWithBackpressureParallel() throws InterruptedException { } final AtomicInteger timeouts = new AtomicInteger(); - final Map> data = new ConcurrentHashMap>(); + final Map> data = new ConcurrentHashMap<>(); int m = 5000; final CountDownLatch cdl = new CountDownLatch(m); @@ -714,11 +714,11 @@ public void run() { final AtomicInteger nexts = new AtomicInteger(); try { Observable origin = Observable.unsafeCreate(new FuncWithErrors(NUM_RETRIES)); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); origin.retry() .observeOn(Schedulers.computation()).subscribe(to); to.awaitDone(2500, TimeUnit.MILLISECONDS); - List onNextEvents = new ArrayList(to.values()); + List onNextEvents = new ArrayList<>(to.values()); if (onNextEvents.size() != NUM_RETRIES + 2) { for (Throwable t : to.errors()) { onNextEvents.add(t.toString()); @@ -816,7 +816,7 @@ public Observable apply(GroupedObservable t1) { return t1.take(1); } }) - .subscribe(new TestObserver(observer)); + .subscribe(new TestObserver<>(observer)); InOrder inOrder = inOrder(observer); // should show 3 attempts @@ -840,7 +840,7 @@ public void issue1900SourceNotSupportingBackpressure() { @Override public void subscribe(Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); for (int i = 0; i < NUM_MSG; i++) { o.onNext("msg:" + count.incrementAndGet()); } @@ -861,7 +861,7 @@ public Observable apply(GroupedObservable t1) { return t1.take(1); } }) - .subscribe(new TestObserver(observer)); + .subscribe(new TestObserver<>(observer)); InOrder inOrder = inOrder(observer); // should show 3 attempts diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java index 38f293cb4a..bce6e9f659 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableRetryWithPredicateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,6 +37,7 @@ import io.reactivex.rxjava3.testsupport.*; public class ObservableRetryWithPredicateTest extends RxJavaTest { + BiPredicate retryTwice = new BiPredicate() { @Override public boolean test(Integer t1, Throwable t2) { @@ -77,7 +78,7 @@ public void retryTwice() { int count; @Override public void subscribe(Observer t1) { - t1.onSubscribe(Disposables.empty()); + t1.onSubscribe(Disposable.empty()); count++; t1.onNext(0); t1.onNext(1); @@ -112,7 +113,7 @@ public void retryTwiceAndGiveUp() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer t1) { - t1.onSubscribe(Disposables.empty()); + t1.onSubscribe(Disposable.empty()); t1.onNext(0); t1.onNext(1); t1.onError(new TestException()); @@ -141,7 +142,7 @@ public void retryOnSpecificException() { int count; @Override public void subscribe(Observer t1) { - t1.onSubscribe(Disposables.empty()); + t1.onSubscribe(Disposable.empty()); count++; t1.onNext(0); t1.onNext(1); @@ -178,7 +179,7 @@ public void retryOnSpecificExceptionAndNotOther() { int count; @Override public void subscribe(Observer t1) { - t1.onSubscribe(Disposables.empty()); + t1.onSubscribe(Disposable.empty()); count++; t1.onNext(0); t1.onNext(1); @@ -235,7 +236,7 @@ public void unsubscribeAfterError() { .unsafeCreate(so) .retry(retry5); - ObservableRetryTest.AsyncObserver async = new ObservableRetryTest.AsyncObserver(observer); + ObservableRetryTest.AsyncObserver async = new ObservableRetryTest.AsyncObserver<>(observer); o.subscribe(async); @@ -262,7 +263,7 @@ public void timeoutWithRetry() { .timeout(80, TimeUnit.MILLISECONDS) .retry(retry5); - ObservableRetryTest.AsyncObserver async = new ObservableRetryTest.AsyncObserver(observer); + ObservableRetryTest.AsyncObserver async = new ObservableRetryTest.AsyncObserver<>(observer); o.subscribe(async); @@ -278,7 +279,7 @@ public void timeoutWithRetry() { @Test public void issue2826() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final RuntimeException e = new RuntimeException("You shall not pass"); final AtomicInteger c = new AtomicInteger(); Observable.just(1).map(new Function() { @@ -312,7 +313,7 @@ public Integer apply(Integer t1) { @Test public void issue3008RetryWithPredicate() { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); final AtomicBoolean isFirst = new AtomicBoolean(true); Observable. just(1L, 2L, 3L).map(new Function() { @Override @@ -340,7 +341,7 @@ public void accept(Long t) { @Test public void issue3008RetryInfinite() { - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); final AtomicBoolean isFirst = new AtomicBoolean(true); Observable. just(1L, 2L, 3L).map(new Function() { @Override @@ -390,6 +391,7 @@ public void dontRetry() { } @Test + @SuppressUndeliverable public void retryDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); @@ -438,6 +440,7 @@ public boolean test(Integer n, Throwable e) throws Exception { } @Test + @SuppressUndeliverable public void retryBiPredicateDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java index 5335a75475..7da1bfcbda 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSampleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -50,7 +50,7 @@ public void sample() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer observer1) { - observer1.onSubscribe(Disposables.empty()); + observer1.onSubscribe(Disposable.empty()); innerScheduler.schedule(new Runnable() { @Override public void run() { @@ -439,4 +439,8 @@ public Observable apply(Observable o) }); } + @Test + public void doubleOnSubscribeObservable() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.sample(Observable.never())); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java index 13b01e6888..c27b2e17c0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScalarXMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,6 +23,7 @@ import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; import io.reactivex.rxjava3.internal.operators.observable.ObservableScalarXMap.ScalarDisposable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.TestHelper; public class ObservableScalarXMapTest extends RxJavaTest { @@ -59,7 +60,7 @@ public Integer get() throws Exception { static final class OneCallablePublisher implements ObservableSource, Supplier { @Override public void subscribe(Observer observer) { - ScalarDisposable sd = new ScalarDisposable(observer, 1); + ScalarDisposable sd = new ScalarDisposable<>(observer, 1); observer.onSubscribe(sd); sd.run(); } @@ -72,7 +73,7 @@ public Integer get() throws Exception { @Test public void tryScalarXMap() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); assertTrue(ObservableScalarXMap.tryScalarXMapSubscribe(new CallablePublisher(), to, new Function>() { @Override public ObservableSource apply(Integer f) throws Exception { @@ -85,7 +86,7 @@ public ObservableSource apply(Integer f) throws Exception { @Test public void emptyXMap() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); assertTrue(ObservableScalarXMap.tryScalarXMapSubscribe(new EmptyCallablePublisher(), to, new Function>() { @Override @@ -99,7 +100,7 @@ public ObservableSource apply(Integer f) throws Exception { @Test public void mapperCrashes() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); assertTrue(ObservableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), to, new Function>() { @Override @@ -113,7 +114,7 @@ public ObservableSource apply(Integer f) throws Exception { @Test public void mapperToJust() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); assertTrue(ObservableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), to, new Function>() { @Override @@ -127,7 +128,7 @@ public ObservableSource apply(Integer f) throws Exception { @Test public void mapperToEmpty() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); assertTrue(ObservableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), to, new Function>() { @Override @@ -141,7 +142,7 @@ public ObservableSource apply(Integer f) throws Exception { @Test public void mapperToCrashingCallable() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); assertTrue(ObservableScalarXMap.tryScalarXMapSubscribe(new OneCallablePublisher(), to, new Function>() { @Override @@ -179,8 +180,8 @@ public ObservableSource apply(Integer v) throws Exception { @Test public void scalarDisposableStateCheck() { - TestObserver to = new TestObserver(); - ScalarDisposable sd = new ScalarDisposable(to, 1); + TestObserver to = new TestObserver<>(); + ScalarDisposable sd = new ScalarDisposable<>(to, 1); to.onSubscribe(sd); assertFalse(sd.isDisposed()); @@ -213,8 +214,8 @@ public void scalarDisposableStateCheck() { @Test public void scalarDisposableRunDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestObserver to = new TestObserver(); - final ScalarDisposable sd = new ScalarDisposable(to, 1); + TestObserver to = new TestObserver<>(); + final ScalarDisposable sd = new ScalarDisposable<>(to, 1); to.onSubscribe(sd); Runnable r1 = new Runnable() { @@ -234,4 +235,13 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void scalarDisposbleWrongFusion() { + TestObserver to = new TestObserver<>(); + final ScalarDisposable sd = new ScalarDisposable<>(to, 1); + to.onSubscribe(sd); + + assertEquals(QueueFuseable.NONE, sd.requestFusion(QueueFuseable.ASYNC)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanTest.java index 5a7d312ede..9a1765ceb3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableScanTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -114,7 +114,7 @@ public Integer apply(Integer t1, Integer t2) { @Test public void shouldNotEmitUntilAfterSubscription() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 100).scan(0, new BiFunction() { @Override @@ -181,7 +181,7 @@ public void seedFactory() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @@ -208,7 +208,7 @@ public Integer apply(Integer t1, Integer t2) { }).take(1); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); o.subscribe(observer); observer.assertValue(0); @@ -220,7 +220,7 @@ public Integer apply(Integer t1, Integer t2) { public void initialValueEmittedNoProducer() { PublishSubject source = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.scan(0, new BiFunction() { @Override @@ -310,7 +310,7 @@ public Object apply(Object a, Object b) throws Exception { public void scanFunctionThrowsAndUpstreamErrorsDoesNotResultInTwoTerminalEvents() { final RuntimeException err = new RuntimeException(); final RuntimeException err2 = new RuntimeException(); - final List list = new CopyOnWriteArrayList(); + final List list = new CopyOnWriteArrayList<>(); final Consumer errorConsumer = new Consumer() { @Override public void accept(Throwable t) throws Exception { @@ -321,7 +321,7 @@ public void accept(Throwable t) throws Exception { Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer o) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); o.onSubscribe(d); o.onNext(1); o.onNext(2); @@ -346,7 +346,7 @@ public void scanFunctionThrowsAndUpstreamCompletesDoesNotResultInTwoTerminalEven Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer o) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); o.onSubscribe(d); o.onNext(1); o.onNext(2); @@ -369,7 +369,7 @@ public void scanFunctionThrowsAndUpstreamEmitsOnNextResultsInScanFunctionBeingCa Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer o) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); o.onSubscribe(d); o.onNext(1); o.onNext(2); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java index 3d805ec79a..645fafbb62 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSequenceEqualTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -336,4 +336,77 @@ public void run() { to.assertEmpty(); } } + + @Test + public void firstCompletesBeforeSecond() { + Observable.sequenceEqual(Observable.just(1), Observable.empty()) + .test() + .assertResult(false); + } + + @Test + public void secondCompletesBeforeFirst() { + Observable.sequenceEqual(Observable.empty(), Observable.just(1)) + .test() + .assertResult(false); + } + + @Test + public void bothEmpty() { + Observable.sequenceEqual(Observable.empty(), Observable.empty()) + .test() + .assertResult(true); + } + + @Test + public void bothJust() { + Observable.sequenceEqual(Observable.just(1), Observable.just(1)) + .test() + .assertResult(true); + } + + @Test + public void bothCompleteWhileComparing() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = Observable.sequenceEqual(ps1, ps2, (a, b) -> { + ps1.onNext(1); + ps1.onComplete(); + + ps2.onNext(1); + ps2.onComplete(); + return a.equals(b); + }) + .test() + ; + + ps1.onNext(0); + ps2.onNext(0); + + to.assertResult(true); + } + + @Test + public void bothCompleteWhileComparingAsObservable() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = Observable.sequenceEqual(ps1, ps2, (a, b) -> { + ps1.onNext(1); + ps1.onComplete(); + + ps2.onNext(1); + ps2.onComplete(); + return a.equals(b); + }) + .toObservable() + .test() + ; + + ps1.onNext(0); + ps2.onNext(0); + + to.assertResult(true); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java index b0023e2989..9aa31211b5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSerializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,10 +20,10 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.observers.DefaultObserver; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -78,7 +78,22 @@ public void multiThreadedBasic() { } @Test - public void multiThreadedWithNPE() { + public void multiThreadedWithNPEFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPE(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPE() { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null); Observable w = Observable.unsafeCreate(onSubscribe); @@ -107,7 +122,22 @@ public void multiThreadedWithNPE() { } @Test - public void multiThreadedWithNPEinMiddle() { + public void multiThreadedWithNPEinMiddleFlaky() throws InterruptedException { + int max = 9; + for (int i = 0; i <= max; i++) { + try { + multiThreadedWithNPEinMiddle(); + return; + } catch (AssertionError ex) { + if (i == max) { + throw ex; + } + } + Thread.sleep((long)(1000 * Math.random() + 100)); + } + } + + void multiThreadedWithNPEinMiddle() { boolean lessThan9 = false; for (int i = 0; i < 3; i++) { TestMultiThreadedObservable onSubscribe = new TestMultiThreadedObservable("one", "two", "three", null, "four", "five", "six", "seven", "eight", "nine"); @@ -221,7 +251,7 @@ static class TestSingleThreadedObservable implements ObservableSource { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); System.out.println("TestSingleThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @@ -272,7 +302,7 @@ private static class TestMultiThreadedObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); System.out.println("TestMultiThreadedObservable subscribed to ..."); final NullPointerException npe = new NullPointerException(); t = new Thread(new Runnable() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleTest.java index 4c7faeaf3e..c511f28baf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -465,7 +465,7 @@ public Integer apply(Integer i1, Integer i2) { @Test public void singleElementOperatorDoNotSwallowExceptionWhenDone() { final Throwable exception = new RuntimeException("some error"); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); try { RxJavaPlugins.setErrorHandler(new Consumer() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTest.java index 7b87b3e329..1a10af31d2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -84,7 +84,7 @@ public void skipLastWithZeroCount() { @Test public void skipLastWithBackpressure() { Observable o = Observable.range(0, Flowable.bufferSize() * 2).skipLast(Flowable.bufferSize() + 10); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o.observeOn(Schedulers.computation()).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertNoErrors(); @@ -92,7 +92,7 @@ public void skipLastWithBackpressure() { } - @Test(expected = IndexOutOfBoundsException.class) + @Test(expected = IllegalArgumentException.class) public void skipLastWithNegativeCount() { Observable.just("one").skipLast(-1); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java index d0a1b24fa2..a4171ea2df 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipLastTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -194,7 +194,7 @@ public ObservableSource apply(Observable o) throws Exception { } @Test - public void onNextDisposeRace() { + public void onCompleteDisposeRace() { TestScheduler scheduler = new TestScheduler(); for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); @@ -219,6 +219,32 @@ public void run() { } } + @Test + public void onCompleteDisposeDelayErrorRace() { + TestScheduler scheduler = new TestScheduler(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final PublishSubject ps = PublishSubject.create(); + + final TestObserver to = ps.skipLast(1, TimeUnit.DAYS, scheduler, true).test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ps.onComplete(); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + to.dispose(); + } + }; + + TestHelper.race(r1, r2); + } + } + @Test public void errorDelayed() { Observable.error(new TestException()) @@ -236,4 +262,177 @@ public void take() { .awaitDone(5, TimeUnit.SECONDS) .assertResult(1); } + + @Test + public void onNextDisposeRace() { + TestScheduler scheduler = new TestScheduler(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final PublishSubject ps = PublishSubject.create(); + + final TestObserver to = ps.skipLast(1, TimeUnit.DAYS, scheduler).test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ps.onNext(1); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + to.dispose(); + } + }; + + TestHelper.race(r1, r2); + } + } + + @Test + public void onNextOnCompleteDisposeDelayErrorRace() { + TestScheduler scheduler = new TestScheduler(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final PublishSubject ps = PublishSubject.create(); + + final TestObserver to = ps.skipLast(1, TimeUnit.DAYS, scheduler, true).test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ps.onNext(1); + ps.onComplete(); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + to.dispose(); + } + }; + + TestHelper.race(r1, r2); + } + } + + @Test + public void skipLastTimedDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + // FIXME the timeunit now matters due to rounding + Observable result = source.skipLast(1000, TimeUnit.MILLISECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(500, TimeUnit.MILLISECONDS); + + source.onNext(4); + source.onNext(5); + source.onNext(6); + + scheduler.advanceTimeBy(950, TimeUnit.MILLISECONDS); + source.onComplete(); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o, never()).onNext(4); + inOrder.verify(o, never()).onNext(5); + inOrder.verify(o, never()).onNext(6); + inOrder.verify(o).onComplete(); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onError(any(Throwable.class)); + } + + @Test + public void skipLastTimedErrorBeforeTimeDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.skipLast(1, TimeUnit.SECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + source.onError(new TestException()); + + scheduler.advanceTimeBy(1050, TimeUnit.MILLISECONDS); + + verify(o).onError(any(TestException.class)); + + verify(o, never()).onComplete(); + verify(o, never()).onNext(any()); + } + + @Test + public void skipLastTimedCompleteBeforeTimeDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.skipLast(1, TimeUnit.SECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(500, TimeUnit.MILLISECONDS); + + source.onComplete(); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onComplete(); + inOrder.verifyNoMoreInteractions(); + + verify(o, never()).onNext(any()); + verify(o, never()).onError(any(Throwable.class)); + } + + @Test + public void skipLastTimedWhenAllElementsAreValidDelayError() { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject source = PublishSubject.create(); + + Observable result = source.skipLast(1, TimeUnit.MILLISECONDS, scheduler, true); + + Observer o = TestHelper.mockObserver(); + + result.subscribe(o); + + source.onNext(1); + source.onNext(2); + source.onNext(3); + + scheduler.advanceTimeBy(500, TimeUnit.MILLISECONDS); + + source.onComplete(); + + InOrder inOrder = inOrder(o); + inOrder.verify(o).onNext(1); + inOrder.verify(o).onNext(2); + inOrder.verify(o).onNext(3); + inOrder.verify(o).onComplete(); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTest.java index d122e23399..c1fe2bf2bc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class ObservableSkipTest extends RxJavaTest { - @Test + @Test(expected = IllegalArgumentException.class) public void skipNegativeElements() { Observable skip = Observable.just("one", "two", "three").skip(-99); @@ -138,7 +138,7 @@ public void skipError() { @Test public void requestOverflowDoesNotOccur() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(1, 10).skip(5).subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTimedTest.java index 1c3fc30e14..83edc3201b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntilTest.java index 290fc2c1f0..bb2c3ef127 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhileTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhileTest.java index ab1abdfa49..b6a0749088 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhileTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSkipWhileTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableStartWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableStartWithTest.java new file mode 100644 index 0000000000..267f5be42a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableStartWithTest.java @@ -0,0 +1,153 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.observable; + +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class ObservableStartWithTest { + + @Test + public void justCompletableComplete() { + Observable.just(1).startWith(Completable.complete()) + .test() + .assertResult(1); + } + + @Test + public void emptyCompletableComplete() { + Observable.empty().startWith(Completable.complete()) + .test() + .assertResult(); + } + + @Test + public void runCompletableError() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run).startWith(Completable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justSingleJust() { + Observable.just(1).startWith(Single.just(2)) + .test() + .assertResult(2, 1); + } + + @Test + public void emptySingleJust() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run) + .startWith(Single.just(2)) + .test() + .assertResult(2); + + verify(run).run(); + } + + @Test + public void runSingleError() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run).startWith(Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justMaybeJust() { + Observable.just(1).startWith(Maybe.just(2)) + .test() + .assertResult(2, 1); + } + + @Test + public void emptyMaybeJust() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run) + .startWith(Maybe.just(2)) + .test() + .assertResult(2); + + verify(run).run(); + } + + @Test + public void runMaybeError() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run).startWith(Maybe.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } + + @Test + public void justObservableJust() { + Observable.just(1).startWith(Observable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5, 1); + } + + @Test + public void emptyObservableJust() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run) + .startWith(Observable.just(2, 3, 4, 5)) + .test() + .assertResult(2, 3, 4, 5); + + verify(run).run(); + } + + @Test + public void emptyObservableEmpty() { + Runnable run = mock(Runnable.class); + Runnable run2 = mock(Runnable.class); + + Observable.fromRunnable(run) + .startWith(Observable.fromRunnable(run2)) + .test() + .assertResult(); + + verify(run).run(); + verify(run2).run(); + } + + @Test + public void runObservableError() { + Runnable run = mock(Runnable.class); + + Observable.fromRunnable(run).startWith(Observable.error(new TestException())) + .test() + .assertFailure(TestException.class); + + verify(run, never()).run(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOnTest.java index 8d2a1706ba..ab3890dcc6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -36,14 +36,14 @@ public void issue813() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch doneLatch = new CountDownLatch(1); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable .unsafeCreate(new ObservableSource() { @Override public void subscribe( final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); scheduled.countDown(); try { try { @@ -74,12 +74,12 @@ public void subscribe( @Test public void onError() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new RuntimeException("fail")); } @@ -147,13 +147,13 @@ public Disposable schedule(@NonNull final Runnable action, final long delayTime, @Test public void unsubscribeInfiniteStream() throws InterruptedException { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger count = new AtomicInteger(); Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer sub) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); sub.onSubscribe(d); for (int i = 1; !d.isDisposed(); i++) { count.incrementAndGet(); @@ -174,7 +174,7 @@ public void subscribe(Observer sub) { public void cancelBeforeActualSubscribe() { TestScheduler test = new TestScheduler(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1).hide() .subscribeOn(test).subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmptyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmptyTest.java index 00948a6e7f..5dd3703c67 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmptyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchIfEmptyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -54,7 +54,7 @@ public void switchWhenEmpty() throws Exception { @Test public void switchTriggerUnsubscribe() throws Exception { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Observable withProducer = Observable.unsafeCreate(new ObservableSource() { @Override @@ -96,7 +96,7 @@ public void onNext(Long aLong) { @Test public void switchShouldTriggerUnsubscribe() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Observable.unsafeCreate(new ObservableSource() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java index dadabeeea3..72e6c93bb8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableSwitchTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,16 +24,17 @@ import org.junit.*; import org.mockito.InOrder; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.internal.util.ExceptionHelper; -import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.PublishSubject; @@ -57,11 +58,11 @@ public void switchWhenOuterCompleteBeforeInner() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> outerObserver) { - outerObserver.onSubscribe(Disposables.empty()); + outerObserver.onSubscribe(Disposable.empty()); publishNext(outerObserver, 50, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 70, "one"); publishNext(innerObserver, 100, "two"); publishCompleted(innerObserver, 200); @@ -86,11 +87,11 @@ public void switchWhenInnerCompleteBeforeOuter() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> outerObserver) { - outerObserver.onSubscribe(Disposables.empty()); + outerObserver.onSubscribe(Disposable.empty()); publishNext(outerObserver, 10, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 0, "one"); publishNext(innerObserver, 10, "two"); publishCompleted(innerObserver, 20); @@ -100,7 +101,7 @@ public void subscribe(Observer innerObserver) { publishNext(outerObserver, 100, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 0, "three"); publishNext(innerObserver, 10, "four"); publishCompleted(innerObserver, 20); @@ -132,11 +133,11 @@ public void switchWithComplete() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> outerObserver) { - outerObserver.onSubscribe(Disposables.empty()); + outerObserver.onSubscribe(Disposable.empty()); publishNext(outerObserver, 50, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 60, "one"); publishNext(innerObserver, 100, "two"); } @@ -145,7 +146,7 @@ public void subscribe(final Observer innerObserver) { publishNext(outerObserver, 200, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 0, "three"); publishNext(innerObserver, 100, "four"); } @@ -191,11 +192,11 @@ public void switchWithError() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> outerObserver) { - outerObserver.onSubscribe(Disposables.empty()); + outerObserver.onSubscribe(Disposable.empty()); publishNext(outerObserver, 50, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 50, "one"); publishNext(innerObserver, 100, "two"); } @@ -204,7 +205,7 @@ public void subscribe(final Observer innerObserver) { publishNext(outerObserver, 200, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 0, "three"); publishNext(innerObserver, 100, "four"); } @@ -250,11 +251,11 @@ public void switchWithSubsequenceComplete() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> outerObserver) { - outerObserver.onSubscribe(Disposables.empty()); + outerObserver.onSubscribe(Disposable.empty()); publishNext(outerObserver, 50, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 50, "one"); publishNext(innerObserver, 100, "two"); } @@ -263,7 +264,7 @@ public void subscribe(Observer innerObserver) { publishNext(outerObserver, 130, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishCompleted(innerObserver, 0); } })); @@ -271,7 +272,7 @@ public void subscribe(Observer innerObserver) { publishNext(outerObserver, 150, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 50, "three"); } })); @@ -304,11 +305,11 @@ public void switchWithSubsequenceError() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); publishNext(observer, 50, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); publishNext(observer, 50, "one"); publishNext(observer, 100, "two"); } @@ -317,7 +318,7 @@ public void subscribe(Observer observer) { publishNext(observer, 130, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); publishError(observer, 0, new TestException()); } })); @@ -325,7 +326,7 @@ public void subscribe(Observer observer) { publishNext(observer, 150, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); publishNext(observer, 50, "three"); } })); @@ -387,11 +388,11 @@ public void switchIssue737() { Observable> source = Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(Observer> outerObserver) { - outerObserver.onSubscribe(Disposables.empty()); + outerObserver.onSubscribe(Disposable.empty()); publishNext(outerObserver, 0, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 10, "1-one"); publishNext(innerObserver, 20, "1-two"); // The following events will be ignored @@ -402,7 +403,7 @@ public void subscribe(Observer innerObserver) { publishNext(outerObserver, 25, Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 10, "2-one"); publishNext(innerObserver, 20, "2-two"); publishNext(innerObserver, 30, "2-three"); @@ -435,7 +436,7 @@ public void unsubscribe() { Observable.unsafeCreate(new ObservableSource>() { @Override public void subscribe(final Observer> observer) { - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); observer.onSubscribe(bs); observer.onNext(Observable.just(1)); isUnsubscribed.set(bs.isDisposed()); @@ -651,12 +652,6 @@ public SingleSource apply(Object v) throws Exception { .assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void switchMapSingleMapperIsNull() { - Observable.just(0) - .switchMapSingle(null); - } - @Test public void switchMapSingleFunctionDoesntReturnSingle() { Observable.just(0) @@ -666,7 +661,7 @@ public SingleSource apply(Object v) throws Exception { return new SingleSource() { @Override public void subscribe(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(1); } }; @@ -870,7 +865,7 @@ public void badMainSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onError(new TestException()); observer.onComplete(); @@ -910,7 +905,7 @@ public void badInnerSource() { .switchMap(Functions.justFunction(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException()); observer.onComplete(); observer.onError(new TestException()); @@ -995,7 +990,7 @@ public void outerInnerErrorRaceIgnoreDispose() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> obs1 = new AtomicReference>(); + final AtomicReference> obs1 = new AtomicReference<>(); final Observable ps1 = new Observable() { @Override protected void subscribeActual( @@ -1003,7 +998,7 @@ protected void subscribeActual( obs1.set(observer); } }; - final AtomicReference> obs2 = new AtomicReference>(); + final AtomicReference> obs2 = new AtomicReference<>(); final Observable ps2 = new Observable() { @Override protected void subscribeActual( @@ -1023,10 +1018,10 @@ public ObservableSource apply(Integer v) throws Exception { }) .test(); - obs1.get().onSubscribe(Disposables.empty()); + obs1.get().onSubscribe(Disposable.empty()); obs1.get().onNext(1); - obs2.get().onSubscribe(Disposables.empty()); + obs2.get().onSubscribe(Disposable.empty()); final TestException ex1 = new TestException(); @@ -1203,7 +1198,7 @@ public Object apply(Integer w) throws Exception { public void undeliverableUponCancel() { List errors = TestHelper.trackPluginErrors(); try { - final TestObserverEx to = new TestObserverEx(); + final TestObserverEx to = new TestObserverEx<>(); Observable.just(1) .map(new Function() { @@ -1256,4 +1251,206 @@ public Observable apply(Integer v) .test() .assertResult(10, 20); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(f -> f.switchMap(v -> Observable.never())); + } + + @Test + public void mainCompleteCancelRace() { + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + AtomicReference> ref = new AtomicReference<>(); + Observable o = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref.set(observer); + } + }; + + TestObserver to = o.switchMap(v -> Observable.never()) + .test(); + + ref.get().onSubscribe(Disposable.empty()); + + TestHelper.race( + () -> ref.get().onComplete(), + () -> to.dispose() + ); + } + } + + @Test + public void mainCompleteInnerErrorRace() { + TestException ex = new TestException(); + + for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + Observable o1 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref1.set(observer); + } + }; + AtomicReference> ref2 = new AtomicReference<>(); + Observable o2 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref2.set(observer); + } + }; + + o1.switchMap(v -> o2) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref2.get().onSubscribe(Disposable.empty()); + + TestHelper.race( + () -> ref1.get().onComplete(), + () -> ref2.get().onError(ex) + ); + } + } + + @Test + public void innerNoSubscriptionYet() { + AtomicReference> ref1 = new AtomicReference<>(); + Observable o1 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref1.set(observer); + } + }; + AtomicReference> ref2 = new AtomicReference<>(); + Observable o2 = new Observable() { + @Override + protected void subscribeActual(@NonNull Observer observer) { + ref2.set(observer); + } + }; + + o1.switchMap(v -> o2) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref1.get().onComplete(); + } + + @Test + public void switchDuringOnNext() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.switchMap(v -> Observable.range(v, 5)) + .doOnNext(v -> { + if (v == 1) { + ps.onNext(2); + } + }) + .test(); + + ps.onNext(1); + + to + .assertValuesOnly(1, 2, 3, 4, 5, 6); + } + + @Test + public void mainCompleteWhileInnerActive() { + PublishSubject ps1 = PublishSubject.create(); + PublishSubject ps2 = PublishSubject.create(); + + TestObserver to = ps1.switchMapDelayError(v -> ps2) + .test(); + + ps1.onNext(1); + ps1.onComplete(); + + ps2.onComplete(); + + to.assertResult(); + } + + @Test + public void innerIgnoresCancelAndErrors() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps + .switchMap(v -> { + if (v == 1) { + return Observable.unsafeCreate(s -> { + s.onSubscribe(Disposable.empty()); + ps.onNext(2); + s.onError(new TestException()); + }); + } + return Observable.never(); + }) + .test(); + + ps.onNext(1); + + to.assertEmpty(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void cancellationShouldTriggerInnerCancellationRace() throws Throwable { + AtomicInteger outer = new AtomicInteger(); + AtomicInteger inner = new AtomicInteger(); + + int n = 10_000; + for (int i = 0; i < n; i++) { + Observable.create(it -> { + it.onNext(0); + }) + .switchMap(v -> createObservable(inner)) + .observeOn(Schedulers.computation()) + .doFinally(() -> { + outer.incrementAndGet(); + }) + .take(1) + .blockingSubscribe(v -> { }, Throwable::printStackTrace); + } + + Thread.sleep(100); + assertEquals(inner.get(), outer.get()); + assertEquals(n, inner.get()); + } + + Observable createObservable(AtomicInteger inner) { + return Observable.unsafeCreate(s -> { + SerializedObserver it = new SerializedObserver<>(s); + it.onSubscribe(Disposable.empty()); + Schedulers.io().scheduleDirect(() -> { + it.onNext(1); + }, 0, TimeUnit.MILLISECONDS); + Schedulers.io().scheduleDirect(() -> { + it.onNext(2); + }, 0, TimeUnit.MILLISECONDS); + }) + .doFinally(() -> { + inner.incrementAndGet(); + }); + } + + @Test + public void innerOnSubscribeOuterCancelRace() { + TestObserver to = new TestObserver(); + + Observable.just(1) + .hide() + .switchMap(v -> Observable.just(1) + .doOnSubscribe(d -> to.dispose()) + .scan(1, (a, b) -> a) + ) + .subscribe(to); + + to.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOneTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOneTest.java index 63161e7fb8..1bd19d6bc9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOneTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastOneTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public class ObservableTakeLastOneTest extends RxJavaTest { @Test public void lastOfManyReturnsLast() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.range(1, 10).takeLast(1).subscribe(to); to.assertValue(10); to.assertNoErrors(); @@ -37,7 +37,7 @@ public void lastOfManyReturnsLast() { @Test public void lastOfEmptyReturnsEmpty() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.empty().takeLast(1).subscribe(to); to.assertNoValues(); to.assertNoErrors(); @@ -46,7 +46,7 @@ public void lastOfEmptyReturnsEmpty() { @Test public void lastOfOneReturnsLast() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1).takeLast(1).subscribe(to); to.assertValue(1); to.assertNoErrors(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTest.java index 90429038e6..f0a342e2e1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -83,14 +83,14 @@ public void takeLastWithZeroCount() { verify(observer, times(1)).onComplete(); } - @Test(expected = IndexOutOfBoundsException.class) + @Test(expected = IllegalArgumentException.class) public void takeLastWithNegativeCount() { Observable.just("one").takeLast(-1); } @Test public void backpressure1() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 100000).takeLast(1) .observeOn(Schedulers.newThread()) .map(newSlowProcessor()).subscribe(to); @@ -101,7 +101,7 @@ public void backpressure1() { @Test public void backpressure2() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 100000).takeLast(Flowable.bufferSize() * 4) .observeOn(Schedulers.newThread()).map(newSlowProcessor()).subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java index a81fd1d9cb..4e2bd6a5c0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeLastTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class ObservableTakeLastTimedTest extends RxJavaTest { - @Test(expected = IndexOutOfBoundsException.class) + @Test(expected = IllegalArgumentException.class) public void takeLastTimedWithNegativeCount() { Observable.just("one").takeLast(-1, 1, TimeUnit.SECONDS); } @@ -300,4 +300,9 @@ public void lastWindowIsFixedInTime() { to.assertResult(1, 2, 3, 4); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.takeLast(1, TimeUnit.SECONDS)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java index 73e2cc33c7..0ecff80446 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableTakeTest extends RxJavaTest { @@ -111,11 +111,12 @@ public Integer apply(Integer t1) { } @Test + @SuppressUndeliverable public void takeDoesntLeakErrors() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("one"); observer.onError(new Throwable("test failed")); } @@ -181,7 +182,7 @@ public void multiTake() { @Override public void subscribe(Observer observer) { - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); observer.onSubscribe(bs); for (int i = 0; !bs.isDisposed(); i++) { System.out.println("Emit: " + i); @@ -214,7 +215,7 @@ static class TestObservableFunc implements ObservableSource { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); System.out.println("TestObservable subscribed to ..."); t = new Thread(new Runnable() { @@ -243,7 +244,7 @@ public void run() { @Override public void subscribe(Observer op) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); op.onSubscribe(d); long l = 1; while (!d.isDisposed()) { @@ -257,7 +258,7 @@ public void subscribe(Observer op) { @Test public void takeObserveOn() { Observer o = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); INFINITE_OBSERVABLE .observeOn(Schedulers.newThread()).take(1).subscribe(to); @@ -272,7 +273,7 @@ public void takeObserveOn() { @Test public void interrupt() throws InterruptedException { - final AtomicReference exception = new AtomicReference(); + final AtomicReference exception = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); Observable.just(1).subscribeOn(Schedulers.computation()).take(1) .subscribe(new Consumer() { @@ -317,7 +318,7 @@ public void onNext(Integer t) { public void reentrantTake() { final PublishSubject source = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.take(1).doOnNext(new Consumer() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTimedTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTimedTest.java index 32f6049afb..7edbfbcf20 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeTimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicateTest.java index b197d0bd89..04d0ab5537 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilPredicateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -141,7 +141,7 @@ public boolean test(Integer v) { @Test public void errorIncludesLastValueAsCause() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final TestException e = new TestException("Forced failure"); Predicate predicate = (new Predicate() { @Override @@ -180,7 +180,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onNext(1); observer.onError(new TestException()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilTest.java index 790af0c882..4bbf042acc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -190,7 +190,7 @@ public void untilFires() { PublishSubject source = PublishSubject.create(); PublishSubject until = PublishSubject.create(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.takeUntil(until).subscribe(to); @@ -217,7 +217,7 @@ public void mainCompletes() { PublishSubject source = PublishSubject.create(); PublishSubject until = PublishSubject.create(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.takeUntil(until).subscribe(to); @@ -242,7 +242,7 @@ public void downstreamUnsubscribes() { PublishSubject source = PublishSubject.create(); PublishSubject until = PublishSubject.create(); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); source.takeUntil(until).take(1).subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java index 7c30166bb3..0a9fc27136 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTakeWhileTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -100,11 +100,12 @@ public boolean test(String input) { } @Test + @SuppressUndeliverable public void takeWhileDoesntLeakErrors() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext("one"); observer.onError(new Throwable("test failed")); } @@ -223,7 +224,7 @@ public boolean test(Integer t1) { return t1 < 2; } }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); source.subscribe(to); @@ -236,7 +237,7 @@ public boolean test(Integer t1) { @Test public void errorCauseIncludesLastValue() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just("abc").takeWhile(new Predicate() { @Override public boolean test(String t1) { @@ -271,7 +272,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onComplete(); observer.onComplete(); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java index 416c248f6a..fe296d572b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,16 +16,15 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import java.util.List; import java.util.concurrent.TimeUnit; +import io.reactivex.rxjava3.functions.Action; import org.junit.*; import org.mockito.InOrder; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -43,12 +42,82 @@ public void before() { observer = TestHelper.mockObserver(); } + @Test + public void throttlingWithDropCallbackCrashes() throws Throwable { + Observable source = Observable.unsafeCreate(new ObservableSource() { + @Override + public void subscribe(Observer innerObserver) { + innerObserver.onSubscribe(Disposable.empty()); + publishNext(innerObserver, 100, "one"); // publish as it's first + publishNext(innerObserver, 300, "two"); // skip as it's last within the first 400 + publishNext(innerObserver, 900, "three"); // publish + publishNext(innerObserver, 905, "four"); // skip + publishCompleted(innerObserver, 1000); // Should be published as soon as the timeout expires. + } + }); + + Action whenDisposed = mock(Action.class); + Observable sampled = source + .doOnDispose(whenDisposed) + .throttleFirst(400, TimeUnit.MILLISECONDS, scheduler, e -> { + if ("two".equals(e)) { + throw new TestException("forced"); + } + }); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + inOrder.verify(observer, times(1)).onError(any(TestException.class)); + inOrder.verify(observer, times(0)).onNext("two"); + inOrder.verify(observer, times(0)).onNext("three"); + inOrder.verify(observer, times(0)).onNext("four"); + inOrder.verify(observer, times(0)).onComplete(); + inOrder.verifyNoMoreInteractions(); + verify(whenDisposed).run(); + } + + @Test + public void throttlingWithDropCallback() { + Observable source = Observable.unsafeCreate(new ObservableSource() { + @Override + public void subscribe(Observer innerObserver) { + innerObserver.onSubscribe(Disposable.empty()); + publishNext(innerObserver, 100, "one"); // publish as it's first + publishNext(innerObserver, 300, "two"); // skip as it's last within the first 400 + publishNext(innerObserver, 900, "three"); // publish + publishNext(innerObserver, 905, "four"); // skip + publishCompleted(innerObserver, 1000); // Should be published as soon as the timeout expires. + } + }); + + Observer dropCallbackObserver = TestHelper.mockObserver(); + Observable sampled = source.throttleFirst(400, TimeUnit.MILLISECONDS, scheduler, dropCallbackObserver::onNext); + sampled.subscribe(observer); + + InOrder inOrder = inOrder(observer); + InOrder dropCallbackOrder = inOrder(dropCallbackObserver); + + scheduler.advanceTimeTo(1000, TimeUnit.MILLISECONDS); + inOrder.verify(observer, times(1)).onNext("one"); + inOrder.verify(observer, times(0)).onNext("two"); + dropCallbackOrder.verify(dropCallbackObserver, times(1)).onNext("two"); + inOrder.verify(observer, times(1)).onNext("three"); + inOrder.verify(observer, times(0)).onNext("four"); + dropCallbackOrder.verify(dropCallbackObserver, times(1)).onNext("four"); + inOrder.verify(observer, times(1)).onComplete(); + inOrder.verifyNoMoreInteractions(); + dropCallbackOrder.verifyNoMoreInteractions(); + } + @Test public void throttlingWithCompleted() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); publishNext(innerObserver, 100, "one"); // publish as it's first publishNext(innerObserver, 300, "two"); // skip as it's last within the first 400 publishNext(innerObserver, 900, "three"); // publish @@ -76,7 +145,7 @@ public void throttlingWithError() { Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); Exception error = new TestException(); publishNext(innerObserver, 100, "one"); // Should be published since it is first publishNext(innerObserver, 200, "two"); // Should be skipped since onError will arrive before the timeout expires @@ -167,28 +236,7 @@ public void dispose() { } @Test - public void badSource() { - List errors = TestHelper.trackPluginErrors(); - try { - new Observable() { - @Override - protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); - observer.onNext(1); - observer.onNext(2); - observer.onComplete(); - observer.onNext(3); - observer.onError(new TestException()); - observer.onComplete(); - } - } - .throttleFirst(1, TimeUnit.DAYS) - .test() - .assertResult(1); - - TestHelper.assertUndeliverable(errors, 0, TestException.class); - } finally { - RxJavaPlugins.reset(); - } + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.throttleFirst(1, TimeUnit.SECONDS)); } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatestTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatestTest.java index 5399f63fce..e1c15f3b1a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatestTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableThrottleLatestTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,12 +20,13 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subjects.PublishSubject; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableThrottleLatestTest extends RxJavaTest { @@ -221,4 +222,424 @@ public void onNext(Integer t) { to.assertResult(1, 2); } + + /** Emit 1, 2, 3, then advance time by a second; 1 and 3 should end up in downstream, 2 should be dropped. */ + @Test + public void onDroppedBasicNoEmitLast() { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserver to = ps.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + to.assertEmpty(); + drops.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onNext(3); + + to.assertValuesOnly(1); + drops.assertValuesOnly(2); + + sch.advanceTimeBy(1, TimeUnit.SECONDS); + + to.assertValuesOnly(1, 3); + drops.assertValuesOnly(2); + + ps.onComplete(); + + to.assertResult(1, 3); + + drops.assertValuesOnly(2); + } + + /** Emit 1, 2, 3; 1 should end up in downstream, 2, 3 should be dropped. */ + @Test + public void onDroppedBasicNoEmitLastDropLast() { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserver to = ps.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + to.assertEmpty(); + drops.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onNext(3); + + to.assertValuesOnly(1); + drops.assertValuesOnly(2); + + ps.onComplete(); + + to.assertResult(1); + + drops.assertValuesOnly(2, 3); + } + + /** Emit 1, 2, 3; 1 and 3 should end up in downstream, 2 should be dropped. */ + @Test + public void onDroppedBasicEmitLast() { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserver to = ps.throttleLatest(1, TimeUnit.SECONDS, sch, true, drops::onNext) + .test(); + + to.assertEmpty(); + drops.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onNext(2); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onNext(3); + + to.assertValuesOnly(1); + drops.assertValuesOnly(2); + + ps.onComplete(); + + to.assertResult(1, 3); + + drops.assertValuesOnly(2); + } + + /** Emit 1, 2, 3; 3 should trigger an error to the downstream because 2 is dropped and the callback crashes. */ + @Test + public void onDroppedBasicNoEmitLastFirstDropCrash() throws Throwable { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserver to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { + if (d == 2) { + throw new TestException("forced"); + } + }) + .test(); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + ps.onNext(2); + + to.assertValuesOnly(1); + + ps.onNext(3); + + to.assertFailure(TestException.class, 1); + + verify(whenDisposed).run(); + } + + /** + * Emit 1, 2, Error; the error should trigger the drop callback and crash it too, + * downstream gets 1, composite(source, drop-crash). + */ + @Test + public void onDroppedBasicNoEmitLastOnErrorDropCrash() throws Throwable { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserverEx to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestObserverEx<>()); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + ps.onNext(2); + + to.assertValuesOnly(1); + + ps.onError(new TestException("source")); + + to.assertFailure(CompositeException.class, 1); + + TestHelper.assertCompositeExceptions(to, TestException.class, "source", TestException.class, "forced 2"); + + verify(whenDisposed, never()).run(); + } + + /** + * Emit 1, 2, 3; 3 should trigger a drop-crash for 2, which then would trigger the error path and drop-crash for 3, + * the last item not delivered, downstream gets 1, composite(drop-crash 2, drop-crash 3). + */ + @Test + public void onDroppedBasicEmitLastOnErrorDropCrash() throws Throwable { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserverEx to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, true, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestObserverEx<>()); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + ps.onNext(2); + + to.assertValuesOnly(1); + + ps.onNext(3); + + to.assertFailure(CompositeException.class, 1); + + TestHelper.assertCompositeExceptions(to, TestException.class, "forced 2", TestException.class, "forced 3"); + + verify(whenDisposed).run(); + } + + /** Emit 1, complete; Downstream gets 1, complete, no drops. */ + @Test + public void onDroppedBasicNoEmitLastNoLastToDrop() { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserver to = ps.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + to.assertEmpty(); + drops.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onComplete(); + + to.assertResult(1); + drops.assertEmpty(); + } + + /** Emit 1, error; Downstream gets 1, error, no drops. */ + @Test + public void onDroppedErrorNoEmitLastNoLastToDrop() { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserver to = ps.throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .test(); + + to.assertEmpty(); + drops.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + ps.onError(new TestException()); + + to.assertFailure(TestException.class, 1); + drops.assertEmpty(); + } + + /** + * Emit 1, 2, complete; complete should crash drop, downstream gets 1, drop-crash 2. + */ + @Test + public void onDroppedHasLastNoEmitLastDropCrash() throws Throwable { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserverEx to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestObserverEx<>()); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + ps.onNext(2); + + to.assertValuesOnly(1); + + ps.onComplete(); + + to.assertFailureAndMessage(TestException.class, "forced 2", 1); + + verify(whenDisposed, never()).run(); + } + + /** + * Emit 1, 2 then dispose the sequence; downstream gets 1, drop should get for 2. + */ + @Test + public void onDroppedDisposeDrops() throws Throwable { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserverEx to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .subscribeWith(new TestObserverEx<>()); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + ps.onNext(2); + + to.assertValuesOnly(1); + + to.dispose(); + + to.assertValuesOnly(1); + drops.assertValuesOnly(2); + + verify(whenDisposed).run(); + } + + /** + * Emit 1 then dispose the sequence; downstream gets 1, drop should not get called. + */ + @Test + public void onDroppedDisposeNoDrops() throws Throwable { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserver drops = new TestObserver<>(); + drops.onSubscribe(Disposable.empty()); + + TestObserverEx to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, drops::onNext) + .subscribeWith(new TestObserverEx<>()); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + to.dispose(); + + to.assertValuesOnly(1); + drops.assertEmpty(); + + verify(whenDisposed).run(); + } + + /** + * Emit 1, 2 then dispose the sequence; downstream gets 1, global error handler should get drop-crash 2. + */ + @Test + public void onDroppedDisposeCrashesDrop() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishSubject ps = PublishSubject.create(); + + TestScheduler sch = new TestScheduler(); + + Action whenDisposed = mock(Action.class); + + TestObserverEx to = ps + .doOnDispose(whenDisposed) + .throttleLatest(1, TimeUnit.SECONDS, sch, false, d -> { throw new TestException("forced " + d); }) + .subscribeWith(new TestObserverEx<>()); + + to.assertEmpty(); + + ps.onNext(1); + + to.assertValuesOnly(1); + + ps.onNext(2); + + to.assertValuesOnly(1); + + to.dispose(); + + to.assertValuesOnly(1); + + verify(whenDisposed).run(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "forced 2"); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeIntervalTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeIntervalTest.java index 7eecc75fb5..ce418200d8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeIntervalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeIntervalTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -60,11 +60,11 @@ public void timeInterval() { subject.onComplete(); inOrder.verify(observer, times(1)).onNext( - new Timed(1, 1000, TIME_UNIT)); + new Timed<>(1, 1000, TIME_UNIT)); inOrder.verify(observer, times(1)).onNext( - new Timed(2, 2000, TIME_UNIT)); + new Timed<>(2, 2000, TIME_UNIT)); inOrder.verify(observer, times(1)).onNext( - new Timed(3, 3000, TIME_UNIT)); + new Timed<>(3, 3000, TIME_UNIT)); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @@ -128,7 +128,6 @@ public void dispose() { TestHelper.checkDisposed(Observable.just(1).timeInterval()); } - @SuppressWarnings("unchecked") @Test public void error() { Observable.error(new TestException()) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTests.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTests.java index f1bb9e7bfe..570eb3de5e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTests.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -51,7 +51,7 @@ public void setUp() { @Test public void shouldNotTimeoutIfOnNextWithinTimeout() { Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); withTimeout.subscribe(to); @@ -66,7 +66,7 @@ public void shouldNotTimeoutIfOnNextWithinTimeout() { @Test public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); withTimeout.subscribe(to); @@ -82,7 +82,7 @@ public void shouldNotTimeoutIfSecondOnNextWithinTimeout() { @Test public void shouldTimeoutIfOnNextNotWithinTimeout() { - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); withTimeout.subscribe(observer); @@ -92,7 +92,7 @@ public void shouldTimeoutIfOnNextNotWithinTimeout() { @Test public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); underlyingSubject.onNext("One"); @@ -104,7 +104,7 @@ public void shouldTimeoutIfSecondOnNextNotWithinTimeout() { @Test public void shouldCompleteIfUnderlyingComletes() { Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); underlyingSubject.onComplete(); @@ -117,7 +117,7 @@ public void shouldCompleteIfUnderlyingComletes() { @Test public void shouldErrorIfUnderlyingErrors() { Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); withTimeout.subscribe(observer); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); underlyingSubject.onError(new UnsupportedOperationException()); @@ -132,7 +132,7 @@ public void shouldSwitchToOtherIfOnNextNotWithinTimeout() { Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); source.subscribe(to); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -155,7 +155,7 @@ public void shouldSwitchToOtherIfOnErrorNotWithinTimeout() { Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); source.subscribe(to); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -178,7 +178,7 @@ public void shouldSwitchToOtherIfOnCompletedNotWithinTimeout() { Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); source.subscribe(to); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -201,7 +201,7 @@ public void shouldSwitchToOtherAndCanBeUnsubscribedIfOnNextNotWithinTimeout() { Observable source = underlyingSubject.timeout(TIMEOUT, TIME_UNIT, testScheduler, other); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); source.subscribe(to); testScheduler.advanceTimeBy(2, TimeUnit.SECONDS); @@ -231,7 +231,7 @@ public void shouldTimeoutIfSynchronizedObservableEmitFirstOnNextNotWithinTimeout final CountDownLatch exit = new CountDownLatch(1); final CountDownLatch timeoutSetuped = new CountDownLatch(1); - final TestObserverEx observer = new TestObserverEx(); + final TestObserverEx observer = new TestObserverEx<>(); new Thread(new Runnable() { @@ -241,7 +241,7 @@ public void run() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); try { timeoutSetuped.countDown(); exit.await(); @@ -280,7 +280,7 @@ public void subscribe(Observer observer) { TestScheduler testScheduler = new TestScheduler(); Observable observableWithTimeout = never.timeout(1000, TimeUnit.MILLISECONDS, testScheduler); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); observableWithTimeout.subscribe(observer); testScheduler.advanceTimeBy(2000, TimeUnit.MILLISECONDS); @@ -360,7 +360,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); @@ -386,7 +386,7 @@ public void badSourceOther() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java index e590ec1663..2c6710cc89 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimeoutWithSelectorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -40,6 +40,7 @@ import io.reactivex.rxjava3.testsupport.*; public class ObservableTimeoutWithSelectorTest extends RxJavaTest { + @Test public void timeoutSelectorNormal1() { PublishSubject source = PublishSubject.create(); @@ -283,7 +284,7 @@ public Observable apply(Integer t1) { return Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); enteredTimeoutOne.countDown(); // force the timeout message be sent after observer.onNext(2) while (true) { @@ -329,7 +330,7 @@ public Void answer(InvocationOnMock invocation) throws Throwable { }).when(o).onComplete(); - final TestObserver to = new TestObserver(o); + final TestObserver to = new TestObserver<>(o); new Thread(new Runnable() { @@ -436,7 +437,7 @@ public void badInnerSource() { .timeout(Functions.justFunction(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onNext(2); observer.onError(new TestException("Second")); @@ -465,7 +466,7 @@ public void badInnerSourceOther() { .timeout(Functions.justFunction(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onNext(2); observer.onError(new TestException("Second")); @@ -493,11 +494,12 @@ public void withOtherMainError() { } @Test + @SuppressUndeliverable public void badSourceTimeout() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onError(new TestException("First")); @@ -564,7 +566,7 @@ public void lateOnTimeoutError() { @Override protected void subscribeActual( Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); sub[count++] = observer; } }; @@ -619,7 +621,7 @@ public void lateOnTimeoutFallbackRace() { protected void subscribeActual( Observer observer) { assertFalse(((Disposable)observer).isDisposed()); - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); sub[count++] = observer; } }; @@ -674,7 +676,7 @@ public void onErrorOnTimeoutRace() { protected void subscribeActual( Observer observer) { assertFalse(((Disposable)observer).isDisposed()); - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); sub[count++] = observer; } }; @@ -729,7 +731,7 @@ public void onCompleteOnTimeoutRace() { protected void subscribeActual( Observer observer) { assertFalse(((Disposable)observer).isDisposed()); - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); sub[count++] = observer; } }; @@ -782,7 +784,7 @@ public void onCompleteOnTimeoutRaceFallback() { protected void subscribeActual( Observer observer) { assertFalse(((Disposable)observer).isDisposed()); - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); sub[count++] = observer; } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimerTest.java index ae1d856b0b..9229d24fe3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,11 +21,11 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import org.mockito.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.operators.observable.ObservableTimer.TimerObserver; @@ -64,7 +64,7 @@ public void timerOnce() { @Test public void timerPeriodically() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.interval(100, 100, TimeUnit.MILLISECONDS, scheduler).subscribe(to); @@ -92,7 +92,7 @@ public void timerPeriodically() { @Test public void interval() { Observable w = Observable.interval(1, TimeUnit.SECONDS, scheduler); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); w.subscribe(to); to.assertNoValues(); @@ -117,8 +117,8 @@ public void interval() { public void withMultipleSubscribersStartingAtSameTime() { Observable w = Observable.interval(1, TimeUnit.SECONDS, scheduler); - TestObserver to1 = new TestObserver(); - TestObserver to2 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); + TestObserver to2 = new TestObserver<>(); w.subscribe(to1); w.subscribe(to2); @@ -154,7 +154,7 @@ public void withMultipleSubscribersStartingAtSameTime() { public void withMultipleStaggeredSubscribers() { Observable w = Observable.interval(1, TimeUnit.SECONDS, scheduler); - TestObserver to1 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); w.subscribe(to1); @@ -162,7 +162,7 @@ public void withMultipleStaggeredSubscribers() { scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - TestObserver to2 = new TestObserver(); + TestObserver to2 = new TestObserver<>(); w.subscribe(to2); @@ -194,7 +194,7 @@ public void withMultipleStaggeredSubscribers() { public void withMultipleStaggeredSubscribersAndPublish() { ConnectableObservable w = Observable.interval(1, TimeUnit.SECONDS, scheduler).publish(); - TestObserver to1 = new TestObserver(); + TestObserver to1 = new TestObserver<>(); w.subscribe(to1); w.connect(); @@ -203,7 +203,7 @@ public void withMultipleStaggeredSubscribersAndPublish() { scheduler.advanceTimeTo(2, TimeUnit.SECONDS); - TestObserver to2 = new TestObserver(); + TestObserver to2 = new TestObserver<>(); w.subscribe(to2); to1.assertValues(0L, 1L); @@ -317,7 +317,7 @@ public void timerDelayZero() { public void timerInterruptible() throws Exception { ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); try { - for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec) }) { + for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec, true) }) { final AtomicBoolean interrupted = new AtomicBoolean(); TestObserver to = Observable.timer(1, TimeUnit.MILLISECONDS, s) .map(new Function() { @@ -348,8 +348,8 @@ public Long apply(Long v) throws Exception { @Test public void cancelledAndRun() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); TimerObserver tm = new TimerObserver(to); tm.dispose(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimestampTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimestampTest.java index 7148a44a4a..084db67473 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimestampTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableTimestampTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -52,9 +52,9 @@ public void timestampWithScheduler() { InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new Timed(1, 0, TimeUnit.MILLISECONDS)); - inOrder.verify(observer, times(1)).onNext(new Timed(2, 100, TimeUnit.MILLISECONDS)); - inOrder.verify(observer, times(1)).onNext(new Timed(3, 200, TimeUnit.MILLISECONDS)); + inOrder.verify(observer, times(1)).onNext(new Timed<>(1, 0, TimeUnit.MILLISECONDS)); + inOrder.verify(observer, times(1)).onNext(new Timed<>(2, 100, TimeUnit.MILLISECONDS)); + inOrder.verify(observer, times(1)).onNext(new Timed<>(3, 200, TimeUnit.MILLISECONDS)); verify(observer, never()).onError(any(Throwable.class)); verify(observer, never()).onComplete(); @@ -76,9 +76,9 @@ public void timestampWithScheduler2() { InOrder inOrder = inOrder(observer); - inOrder.verify(observer, times(1)).onNext(new Timed(1, 0, TimeUnit.MILLISECONDS)); - inOrder.verify(observer, times(1)).onNext(new Timed(2, 0, TimeUnit.MILLISECONDS)); - inOrder.verify(observer, times(1)).onNext(new Timed(3, 200, TimeUnit.MILLISECONDS)); + inOrder.verify(observer, times(1)).onNext(new Timed<>(1, 0, TimeUnit.MILLISECONDS)); + inOrder.verify(observer, times(1)).onNext(new Timed<>(2, 0, TimeUnit.MILLISECONDS)); + inOrder.verify(observer, times(1)).onNext(new Timed<>(3, 200, TimeUnit.MILLISECONDS)); verify(observer, never()).onError(any(Throwable.class)); verify(observer, never()).onComplete(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToFutureTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToFutureTest.java index 481fd8ae65..0ac13be46a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToFutureTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToFutureTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public void success() throws Exception { Observer o = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); Observable.fromFuture(future).subscribe(to); @@ -59,9 +59,9 @@ public void successOperatesOnSuppliedScheduler() throws Exception { Observer o = TestHelper.mockObserver(); TestScheduler scheduler = new TestScheduler(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); - Observable.fromFuture(future, scheduler).subscribe(to); + Observable.fromFuture(future).subscribeOn(scheduler).subscribe(to); verify(o, never()).onNext(value); @@ -79,7 +79,7 @@ public void failure() throws Exception { Observer o = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); Observable.fromFuture(future).subscribe(to); @@ -100,7 +100,7 @@ public void cancelledBeforeSubscribe() throws Exception { Observer o = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); to.dispose(); Observable.fromFuture(future).subscribe(to); @@ -146,7 +146,7 @@ public Object get(long timeout, TimeUnit unit) throws InterruptedException, Exec Observer o = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o); + TestObserver to = new TestObserver<>(o); Observable futureObservable = Observable.fromFuture(future); futureObservable.subscribeOn(Schedulers.computation()).subscribe(to); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListTest.java index 1981c2b5af..2d4185407d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToListTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -85,7 +85,6 @@ public void listWithBlockingFirstObservable() { Assert.assertEquals(Arrays.asList("one", "two", "three"), actual); } - @SuppressWarnings("unchecked") @Test public void capacityHintObservable() { Observable.range(1, 10) @@ -154,7 +153,6 @@ static void await(CyclicBarrier cb) { } } - @SuppressWarnings("unchecked") @Test public void capacityHint() { Observable.range(1, 10) @@ -170,7 +168,6 @@ public void dispose() { TestHelper.checkDisposed(Observable.just(1).toList()); } - @SuppressWarnings("unchecked") @Test public void error() { Observable.error(new TestException()) @@ -180,7 +177,6 @@ public void error() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorSingle() { Observable.error(new TestException()) @@ -189,7 +185,6 @@ public void errorSingle() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void collectionSupplierThrows() { Observable.just(1) @@ -204,7 +199,6 @@ public Collection get() throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void collectionSupplierReturnsNull() { Observable.just(1) @@ -220,7 +214,6 @@ public Collection get() throws Exception { .assertErrorMessage(ExceptionHelper.nullWarning("The collectionSupplier returned a null Collection.")); } - @SuppressWarnings("unchecked") @Test public void singleCollectionSupplierThrows() { Observable.just(1) @@ -234,7 +227,6 @@ public Collection get() throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void singleCollectionSupplierReturnsNull() { Observable.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMapTest.java index 25136fcd9d..72b39d0025 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -55,7 +55,7 @@ public void toMapObservable() { Observable> mapped = source.toMap(lengthFunc).toObservable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -74,7 +74,7 @@ public void toMapWithValueSelectorObservable() { Observable> mapped = source.toMap(lengthFunc, duplicate).toObservable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -102,7 +102,7 @@ public Integer apply(String t1) { }; Observable> mapped = source.toMap(lengthFuncErr).toObservable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -132,7 +132,7 @@ public String apply(String t1) { Observable> mapped = source.toMap(lengthFunc, duplicateErr).toObservable(); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -178,7 +178,7 @@ public String apply(String v) { } }, mapFactory).toObservable(); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); @@ -214,7 +214,7 @@ public String apply(String v) { } }, mapFactory).toObservable(); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); @@ -232,7 +232,7 @@ public void toMap() { Single> mapped = source.toMap(lengthFunc); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -250,7 +250,7 @@ public void toMapWithValueSelector() { Single> mapped = source.toMap(lengthFunc, duplicate); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -277,7 +277,7 @@ public Integer apply(String t1) { }; Single> mapped = source.toMap(lengthFuncErr); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "a"); expected.put(2, "bb"); expected.put(3, "ccc"); @@ -306,7 +306,7 @@ public String apply(String t1) { Single> mapped = source.toMap(lengthFunc, duplicateErr); - Map expected = new HashMap(); + Map expected = new HashMap<>(); expected.put(1, "aa"); expected.put(2, "bbbb"); expected.put(3, "cccccc"); @@ -351,7 +351,7 @@ public String apply(String v) { } }, mapFactory); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); @@ -386,7 +386,7 @@ public String apply(String v) { } }, mapFactory); - Map expected = new LinkedHashMap(); + Map expected = new LinkedHashMap<>(); expected.put(2, "bb"); expected.put(3, "ccc"); expected.put(4, "dddd"); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMultimapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMultimapTest.java index ef7f0feec4..10bda16080 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMultimapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToMultimapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -55,7 +55,7 @@ public void toMultimapObservable() { Observable>> mapped = source.toMultimap(lengthFunc).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -72,7 +72,7 @@ public void toMultimapWithValueSelectorObservable() { Observable>> mapped = source.toMultimap(lengthFunc, duplicate).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -114,11 +114,11 @@ public String apply(String v) { mapFactory, new Function>() { @Override public Collection apply(Integer v) { - return new ArrayList(); + return new ArrayList<>(); } }).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -137,9 +137,9 @@ public void toMultimapWithCollectionFactoryObservable() { @Override public Collection apply(Integer t1) { if (t1 == 2) { - return new ArrayList(); + return new ArrayList<>(); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -153,16 +153,16 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Observable>> mapped = source .toMultimap(lengthFunc, identity, mapSupplier, collectionFactory).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); - expected.put(3, new HashSet(Arrays.asList("eee"))); + expected.put(3, new HashSet<>(Arrays.asList("eee"))); mapped.subscribe(objectObserver); @@ -187,7 +187,7 @@ public Integer apply(String t1) { Observable>> mapped = source.toMultimap(lengthFuncErr).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -214,7 +214,7 @@ public String apply(String t1) { Observable>> mapped = source.toMultimap(lengthFunc, duplicateErr).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -244,7 +244,7 @@ public String apply(String v) { } }, mapFactory).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -265,7 +265,7 @@ public Collection apply(Integer t1) { if (t1 == 2) { throw new RuntimeException("Forced failure"); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -279,14 +279,14 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Observable>> mapped = source.toMultimap(lengthFunc, identity, mapSupplier, collectionFactory).toObservable(); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Collections.singleton("eee")); @@ -303,7 +303,7 @@ public void toMultimap() { Single>> mapped = source.toMultimap(lengthFunc); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -319,7 +319,7 @@ public void toMultimapWithValueSelector() { Single>> mapped = source.toMultimap(lengthFunc, duplicate); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -360,11 +360,11 @@ public String apply(String v) { mapFactory, new Function>() { @Override public Collection apply(Integer v) { - return new ArrayList(); + return new ArrayList<>(); } }); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -382,9 +382,9 @@ public void toMultimapWithCollectionFactory() { @Override public Collection apply(Integer t1) { if (t1 == 2) { - return new ArrayList(); + return new ArrayList<>(); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -398,16 +398,16 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Single>> mapped = source .toMultimap(lengthFunc, identity, mapSupplier, collectionFactory); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); - expected.put(3, new HashSet(Arrays.asList("eee"))); + expected.put(3, new HashSet<>(Arrays.asList("eee"))); mapped.subscribe(singleObserver); @@ -431,7 +431,7 @@ public Integer apply(String t1) { Single>> mapped = source.toMultimap(lengthFuncErr); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("a", "b")); expected.put(2, Arrays.asList("cc", "dd")); @@ -457,7 +457,7 @@ public String apply(String t1) { Single>> mapped = source.toMultimap(lengthFunc, duplicateErr); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(1, Arrays.asList("aa", "bb")); expected.put(2, Arrays.asList("cccc", "dddd")); @@ -486,7 +486,7 @@ public String apply(String v) { } }, mapFactory); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Arrays.asList("eee", "fff")); @@ -506,7 +506,7 @@ public Collection apply(Integer t1) { if (t1 == 2) { throw new RuntimeException("Forced failure"); } else { - return new HashSet(); + return new HashSet<>(); } } }; @@ -520,14 +520,14 @@ public String apply(String v) { Supplier>> mapSupplier = new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }; Single>> mapped = source.toMultimap(lengthFunc, identity, mapSupplier, collectionFactory); - Map> expected = new HashMap>(); + Map> expected = new HashMap<>(); expected.put(2, Arrays.asList("cc", "dd")); expected.put(3, Collections.singleton("eee")); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToSortedListTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToSortedListTest.java index 7d01bcc3b1..0ea0be21b9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToSortedListTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToSortedListTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -96,7 +96,6 @@ public int compare(Integer a, Integer b) { .assertResult(5, 4, 3, 2, 1); } - @SuppressWarnings("unchecked") @Test public void toSortedListCapacityObservable() { Observable.just(5, 1, 2, 4, 3).toSortedList(4).toObservable() @@ -104,7 +103,6 @@ public void toSortedListCapacityObservable() { .assertResult(Arrays.asList(1, 2, 3, 4, 5)); } - @SuppressWarnings("unchecked") @Test public void toSortedListComparatorCapacityObservable() { Observable.just(5, 1, 2, 4, 3).toSortedList(new Comparator() { @@ -152,7 +150,6 @@ public void withFollowingFirst() { assertEquals(Arrays.asList(1, 2, 3, 4, 5), o.toSortedList().blockingGet()); } - @SuppressWarnings("unchecked") @Test public void toSortedListCapacity() { Observable.just(5, 1, 2, 4, 3).toSortedList(4) @@ -160,7 +157,6 @@ public void toSortedListCapacity() { .assertResult(Arrays.asList(1, 2, 3, 4, 5)); } - @SuppressWarnings("unchecked") @Test public void toSortedListComparatorCapacity() { Observable.just(5, 1, 2, 4, 3).toSortedList(new Comparator() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToXTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToXTest.java index 1afbba0225..c7cc6f3151 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToXTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableToXTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java index ce8eee9b50..23f6d7256b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUnsubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,6 +26,7 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.internal.schedulers.ImmediateThinScheduler; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -37,7 +38,7 @@ public void unsubscribeWhenSubscribeOnAndUnsubscribeOnAreOnSameThread() throws I UIEventLoopScheduler uiEventLoop = new UIEventLoopScheduler(); try { final ThreadSubscription subscription = new ThreadSubscription(); - final AtomicReference subscribeThread = new AtomicReference(); + final AtomicReference subscribeThread = new AtomicReference<>(); Observable w = Observable.unsafeCreate(new ObservableSource() { @Override @@ -53,7 +54,7 @@ public void subscribe(Observer t1) { } }); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); w.subscribeOn(uiEventLoop).observeOn(Schedulers.computation()) .unsubscribeOn(uiEventLoop) @@ -87,7 +88,7 @@ public void unsubscribeWhenSubscribeOnAndUnsubscribeOnAreOnDifferentThreads() th UIEventLoopScheduler uiEventLoop = new UIEventLoopScheduler(); try { final ThreadSubscription subscription = new ThreadSubscription(); - final AtomicReference subscribeThread = new AtomicReference(); + final AtomicReference subscribeThread = new AtomicReference<>(); Observable w = Observable.unsafeCreate(new ObservableSource() { @Override @@ -103,7 +104,7 @@ public void subscribe(Observer t1) { } }); - TestObserverEx observer = new TestObserverEx(); + TestObserverEx observer = new TestObserverEx<>(); w.subscribeOn(Schedulers.newThread()).observeOn(Schedulers.computation()) .unsubscribeOn(uiEventLoop) @@ -248,7 +249,7 @@ public void signalAfterDispose() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onError(new TestException()); @@ -265,4 +266,9 @@ protected void subscribeActual(Observer observer) { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.unsubscribeOn(ImmediateThinScheduler.INSTANCE)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsingTest.java index 62e8986292..de8d73fa36 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableUsingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -209,7 +209,7 @@ private void performTestUsingWithObservableFactoryError(boolean disposeEagerly) Supplier resourceFactory = new Supplier() { @Override public Disposable get() { - return Disposables.fromRunnable(unsubscribe); + return Disposable.fromRunnable(unsubscribe); } }; @@ -232,7 +232,7 @@ public Observable apply(Disposable subscription) { @Test public void usingDisposesEagerlyBeforeCompletion() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); Supplier resourceFactory = createResourceFactory(events); final Action completion = createOnCompletedAction(events); final Action unsub = createUnsubAction(events); @@ -259,7 +259,7 @@ public Observable apply(Resource resource) { @Test public void usingDoesNotDisposesEagerlyBeforeCompletion() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); Supplier resourceFactory = createResourceFactory(events); final Action completion = createOnCompletedAction(events); final Action unsub = createUnsubAction(events); @@ -286,7 +286,7 @@ public Observable apply(Resource resource) { @Test public void usingDisposesEagerlyBeforeError() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); Supplier resourceFactory = createResourceFactory(events); final Consumer onError = createOnErrorAction(events); final Action unsub = createUnsubAction(events); @@ -314,7 +314,7 @@ public Observable apply(Resource resource) { @Test public void usingDoesNotDisposesEagerlyBeforeError() { - final List events = new ArrayList(); + final List events = new ArrayList<>(); final Supplier resourceFactory = createResourceFactory(events); final Consumer onError = createOnErrorAction(events); final Action unsub = createUnsubAction(events); @@ -533,12 +533,12 @@ public ObservableSource apply(Observable o) @Test public void eagerDisposedOnComplete() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.using(Functions.justSupplier(1), Functions.justFunction(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); to.dispose(); observer.onComplete(); } @@ -548,12 +548,12 @@ protected void subscribeActual(Observer observer) { @Test public void eagerDisposedOnError() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Observable.using(Functions.justSupplier(1), Functions.justFunction(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); to.dispose(); observer.onError(new TestException()); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java index 1ac8c7209b..edfca3989d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; -import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -44,7 +44,7 @@ public void windowViaObservableNormal1() { final Observer o = TestHelper.mockObserver(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Observer> wo = new DefaultObserver>() { @Override @@ -101,7 +101,7 @@ public void windowViaObservableBoundaryCompletes() { final Observer o = TestHelper.mockObserver(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Observer> wo = new DefaultObserver>() { @Override @@ -157,7 +157,7 @@ public void windowViaObservableBoundaryThrows() { final Observer o = TestHelper.mockObserver(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Observer> wo = new DefaultObserver>() { @Override @@ -207,7 +207,7 @@ public void windowViaObservableSourceThrows() { final Observer o = TestHelper.mockObserver(); - final List> values = new ArrayList>(); + final List> values = new ArrayList<>(); Observer> wo = new DefaultObserver>() { @Override @@ -368,16 +368,22 @@ public ObservableSource apply( public void mainAndBoundaryBothError() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> ref = new AtomicReference<>(); TestObserverEx> to = Observable.error(new TestException("main")) .window(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) .to(TestHelper.>testConsumer()); to @@ -401,20 +407,20 @@ public void mainCompleteBoundaryErrorRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); TestObserverEx> to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); refMain.set(observer); } } .window(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }) @@ -451,20 +457,20 @@ public void run() { @Test public void mainNextBoundaryNextRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); TestObserver> to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); refMain.set(observer); } } .window(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }) @@ -494,20 +500,20 @@ public void run() { @Test public void takeOneAnotherBoundary() { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); TestObserverEx> to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); refMain.set(observer); } } .window(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); ref.set(observer); } }) @@ -526,13 +532,13 @@ protected void subscribeActual(Observer observer) { @Test public void disposeMainBoundaryCompleteRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); final TestObserver> to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); refMain.set(observer); } } @@ -580,17 +586,18 @@ public void run() { } @Test + @SuppressUndeliverable public void disposeMainBoundaryErrorRace() { final TestException ex = new TestException(); for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - final AtomicReference> refMain = new AtomicReference>(); - final AtomicReference> ref = new AtomicReference>(); + final AtomicReference> refMain = new AtomicReference<>(); + final AtomicReference> ref = new AtomicReference<>(); final TestObserver> to = new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); refMain.set(observer); } } @@ -636,4 +643,65 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void cancellingWindowCancelsUpstream() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(Observable.never()) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstream() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(Observable.never()) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + to + .assertValueCount(1) + ; + + ps.onNext(1); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + to + .assertValueCount(1) + .assertNoErrors() + .assertNotComplete(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + inner.get().test().assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java index e90d46bd36..81d076e718 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithSizeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,7 +17,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; @@ -36,8 +36,8 @@ public class ObservableWindowWithSizeTest extends RxJavaTest { private static List> toLists(Observable> observables) { - final List> lists = new ArrayList>(); - Observable.concat(observables.map(new Function, Observable>>() { + final List> lists = new ArrayList<>(); + Observable.concatEager(observables.map(new Function, Observable>>() { @Override public Observable> apply(Observable xs) { return xs.toList().toObservable(); @@ -106,7 +106,7 @@ public void skipAndCountWindowsWithGaps() { @Test public void windowUnsubscribeNonOverlapping() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final AtomicInteger count = new AtomicInteger(); Observable.merge(Observable.range(1, 10000).doOnNext(new Consumer() { @@ -128,7 +128,7 @@ public void accept(Integer t1) { @Test public void windowUnsubscribeNonOverlappingAsyncSource() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final AtomicInteger count = new AtomicInteger(); Observable.merge(Observable.range(1, 100000) @@ -161,7 +161,7 @@ public void accept(Integer t1) { @Test public void windowUnsubscribeOverlapping() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final AtomicInteger count = new AtomicInteger(); Observable.merge(Observable.range(1, 10000).doOnNext(new Consumer() { @@ -183,7 +183,7 @@ public void accept(Integer t1) { @Test public void windowUnsubscribeOverlappingAsyncSource() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); final AtomicInteger count = new AtomicInteger(); Observable.merge(Observable.range(1, 100000) @@ -208,7 +208,7 @@ public void accept(Integer t1) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -219,7 +219,7 @@ public static Observable hotStream() { return Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); while (!d.isDisposed()) { // burst some number of items @@ -241,7 +241,7 @@ public void subscribe(Observer observer) { @Test public void takeFlatMapCompletes() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final int indicator = 999999999; @@ -293,7 +293,6 @@ public ObservableSource> apply(Observable o) throws E }); } - @SuppressWarnings("unchecked") @Test public void errorExact() { Observable.error(new TestException()) @@ -302,7 +301,6 @@ public void errorExact() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorSkip() { Observable.error(new TestException()) @@ -311,7 +309,6 @@ public void errorSkip() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void errorOverlap() { Observable.error(new TestException()) @@ -376,4 +373,256 @@ public void accept(Observable w) throws Exception { to[0].assertFailure(TestException.class, 1); } + + @Test + public void cancellingWindowCancelsUpstreamSize() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(10) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstreamSize() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(10) + .take(1) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + inner.get().test().assertResult(1); + } + + @Test + public void cancellingWindowCancelsUpstreamSkip() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(5, 10) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstreamSkip() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(5, 10) + .take(1) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + inner.get().test().assertResult(1); + } + + @Test + public void cancellingWindowCancelsUpstreamOverlap() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(5, 3) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstreamOverlap() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(5, 3) + .take(1) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + inner.get().test().assertResult(1); + } + + @Test + public void cancelWithoutWindowSize() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10) + .test(); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelAfterAbandonmentSize() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelWithoutWindowSkip() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 15) + .test(); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelAfterAbandonmentSkip() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 15) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelWithoutWindowOverlap() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 5) + .test(); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void cancelAfterAbandonmentOverlap() { + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(10, 5) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to.dispose(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java index 90732ddf1e..659daaf7d1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithStartEndObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -15,16 +15,17 @@ import static org.junit.Assert.*; +import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.*; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -32,7 +33,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.TestScheduler; import io.reactivex.rxjava3.subjects.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableWindowWithStartEndObservableTest extends RxJavaTest { @@ -47,13 +48,13 @@ public void before() { @Test public void observableBasedOpenerAndCloser() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); push(innerObserver, "one", 10); push(innerObserver, "two", 60); push(innerObserver, "three", 110); @@ -66,7 +67,7 @@ public void subscribe(Observer innerObserver) { Observable openings = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); push(innerObserver, new Object(), 50); push(innerObserver, new Object(), 200); complete(innerObserver, 250); @@ -79,7 +80,7 @@ public Observable apply(Object opening) { return Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer innerObserver) { - innerObserver.onSubscribe(Disposables.empty()); + innerObserver.onSubscribe(Disposable.empty()); push(innerObserver, new Object(), 100); complete(innerObserver, 101); } @@ -97,7 +98,7 @@ public void subscribe(Observer innerObserver) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -129,7 +130,7 @@ public void accept(Observable stringObservable) { stringObservable.subscribe(new DefaultObserver() { @Override public void onComplete() { - lists.add(new ArrayList(list)); + lists.add(new ArrayList<>(list)); list.clear(); } @@ -154,14 +155,21 @@ public void noUnsubscribeAndNoLeak() { PublishSubject open = PublishSubject.create(); final PublishSubject close = PublishSubject.create(); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); source.window(open, new Function>() { @Override public Observable apply(Integer t) { return close; } - }).subscribe(to); + }) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) + .subscribe(to); open.onNext(1); source.onNext(1); @@ -192,14 +200,21 @@ public void unsubscribeAll() { PublishSubject open = PublishSubject.create(); final PublishSubject close = PublishSubject.create(); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); source.window(open, new Function>() { @Override public Observable apply(Integer t) { return close; } - }).subscribe(to); + }) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) + .subscribe(to); open.onNext(1); @@ -275,6 +290,7 @@ public ObservableSource apply(Integer v) throws Exception { } @Test + @SuppressUndeliverable public void endError() { PublishSubject source = PublishSubject.create(); PublishSubject start = PublishSubject.create(); @@ -357,7 +373,7 @@ public Observable apply(Integer f) throws Exception { @Override protected void subscribeActual( Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onNext(2); observer.onError(new TestException()); @@ -365,6 +381,12 @@ protected void subscribeActual( }; } }) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) .test() .assertValueCount(1) .assertNoErrors() @@ -399,6 +421,12 @@ public ObservableSource apply(Integer v) throws Exception { return observableDisposed(closeDisposed); } }) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable w) throws Throwable { + w.subscribe(Functions.emptyConsumer(), Functions.emptyConsumer()); // avoid abandonment + } + }) .to(TestHelper.>testConsumer()) .assertSubscribed() .assertNoErrors() @@ -409,4 +437,269 @@ public ObservableSource apply(Integer v) throws Exception { assertTrue(openDisposed.get()); assertTrue(closeDisposed.get()); } + + @Test + public void cancellingWindowCancelsUpstream() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(Observable.just(1).concatWith(Observable.never()), Functions.justFunction(Observable.never())) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstream() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(Observable.just(1).concatWith(Observable.never()), + Functions.justFunction(Observable.never())) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + to + .assertValueCount(1) + ; + + ps.onNext(1); + + assertTrue(ps.hasObservers()); + + to.dispose(); + + to + .assertValueCount(1) + .assertNoErrors() + .assertNotComplete(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + inner.get().test().assertResult(); + } + + @Test + public void closingIndicatorFunctionCrash() { + + PublishSubject source = PublishSubject.create(); + PublishSubject boundary = PublishSubject.create(); + + TestObserver> to = source.window(boundary, new Function>() { + @Override + public Observable apply(Integer end) throws Throwable { + throw new TestException(); + } + }) + .test() + ; + + to.assertEmpty(); + + boundary.onNext(1); + + to.assertFailure(TestException.class); + + assertFalse(source.hasObservers()); + assertFalse(boundary.hasObservers()); + } + + @Test + public void mainError() { + Observable.error(new TestException()) + .window(Observable.never(), Functions.justFunction(Observable.never())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.window(Observable.never(), v -> Observable.never())); + } + + @Test + public void openError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestException ex1 = new TestException(); + TestException ex2 = new TestException(); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Observable o1 = Observable.unsafeCreate(ref1::set); + Observable o2 = Observable.unsafeCreate(ref2::set); + + TestObserver> to = BehaviorSubject.createDefault(1) + .window(o1, v -> o2) + .doOnNext(w -> w.test()) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref2.get().onSubscribe(Disposable.empty()); + + TestHelper.race( + () -> ref1.get().onError(ex1), + () -> ref2.get().onError(ex2) + ); + + to.assertError(RuntimeException.class); + + if (!errors.isEmpty()) { + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } + + errors.clear(); + } + }); + } + + @Test + public void closeError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Observable o1 = Observable.unsafeCreate(ref1::set); + Observable o2 = Observable.unsafeCreate(ref2::set); + + TestObserver to = BehaviorSubject.createDefault(1) + .window(o1, v -> o2) + .flatMap(v -> v) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref2.get().onSubscribe(Disposable.empty()); + + ref2.get().onError(new TestException()); + ref2.get().onError(new TestException()); + + to.assertFailure(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void upstreamFailsBeforeFirstWindow() { + Observable.error(new TestException()) + .window(Observable.never(), v -> Observable.never()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void windowOpenMainCompletes() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishSubject ps = PublishSubject.create(); + Observable o1 = Observable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestObserver> to = ps + .window(o1, v -> Observable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + ps.onNext(1); + ps.onComplete(); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + + to.assertComplete(); + } + + @Test + public void windowOpenMainError() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishSubject ps = PublishSubject.create(); + Observable o1 = Observable.unsafeCreate(ref1::set); + + AtomicInteger counter = new AtomicInteger(); + + TestObserver> to = ps + .window(o1, v -> Observable.never()) + .doOnNext(w -> { + if (counter.getAndIncrement() == 0) { + ref1.get().onNext(2); + ps.onNext(1); + ps.onError(new TestException()); + } + w.test(); + }) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + + to.assertError(TestException.class); + } + + @Test + public void windowOpenIgnoresDispose() { + AtomicReference> ref1 = new AtomicReference<>(); + + PublishSubject ps = PublishSubject.create(); + Observable o1 = Observable.unsafeCreate(ref1::set); + + TestObserver> to = ps + .window(o1, v -> Observable.never()) + .take(1) + .doOnNext(w -> { + w.test(); + }) + .test(); + + ref1.get().onSubscribe(Disposable.empty()); + ref1.get().onNext(1); + ref1.get().onNext(2); + + to.assertValueCount(1); + } + + @Test + public void mainIgnoresCancelBeforeOnError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Observable.unsafeCreate(s -> { + s.onSubscribe(Disposable.empty()); + s.onNext(1); + s.onError(new IOException()); + }) + .window(BehaviorSubject.createDefault(1), v -> Observable.error(new TestException())) + .doOnNext(w -> w.test()) + .test() + .assertError(TestException.class); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java index ba9544eb9c..ca8f90d04f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWindowWithTimeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,14 +24,14 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.*; import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ObservableWindowWithTimeTest extends RxJavaTest { @@ -46,13 +46,13 @@ public void before() { @Test public void timedAndCount() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, "one", 10); push(observer, "two", 90); push(observer, "three", 110); @@ -82,13 +82,13 @@ public void subscribe(Observer observer) { @Test public void timed() { - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); Observable source = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); push(observer, "one", 98); push(observer, "two", 99); push(observer, "three", 99); // FIXME happens after the window is open @@ -111,7 +111,7 @@ public void subscribe(Observer observer) { } private List list(String... args) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (String arg : args) { list.add(arg); } @@ -143,7 +143,7 @@ public void accept(Observable stringObservable) { stringObservable.subscribe(new DefaultObserver() { @Override public void onComplete() { - lists.add(new ArrayList(list)); + lists.add(new ArrayList<>(list)); list.clear(); } @@ -166,8 +166,8 @@ public void exactWindowSize() { Observable> source = Observable.range(1, 10) .window(1, TimeUnit.MINUTES, scheduler, 3); - final List list = new ArrayList(); - final List> lists = new ArrayList>(); + final List list = new ArrayList<>(); + final List> lists = new ArrayList<>(); source.subscribe(observeWindow(list, lists)); @@ -184,7 +184,7 @@ public void exactWindowSize() { @Test public void takeFlatMapCompletes() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); final AtomicInteger wip = new AtomicInteger(); @@ -368,6 +368,7 @@ public void timeskipOverlapping() { } @Test + @SuppressUndeliverable public void exactOnError() { TestScheduler scheduler = new TestScheduler(); @@ -383,6 +384,7 @@ public void exactOnError() { } @Test + @SuppressUndeliverable public void overlappingOnError() { TestScheduler scheduler = new TestScheduler(); @@ -398,6 +400,7 @@ public void overlappingOnError() { } @Test + @SuppressUndeliverable public void skipOnError() { TestScheduler scheduler = new TestScheduler(); @@ -434,6 +437,7 @@ public void restartTimer() { } @Test + @SuppressUndeliverable public void exactBoundaryError() { Observable.error(new TestException()) .window(1, TimeUnit.DAYS, Schedulers.single(), 2, true) @@ -751,6 +755,7 @@ public void accept(Observable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -831,6 +836,7 @@ public void accept(Observable v) throws Exception { } @Test + @SuppressUndeliverable public void exactTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -911,6 +917,7 @@ public void accept(Observable v) throws Exception { } @Test + @SuppressUndeliverable public void skipTimeAndSizeBoundNoInterruptWindowOutputOnError() throws Exception { final AtomicBoolean isInterrupted = new AtomicBoolean(); @@ -949,4 +956,176 @@ public void accept(Observable v) throws Exception { assertFalse("The doOnNext got interrupted!", isInterrupted.get()); } + + @Test + public void cancellingWindowCancelsUpstreamExactTime() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(10, TimeUnit.MINUTES) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstreamExactTime() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(10, TimeUnit.MINUTES) + .take(1) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + to + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + inner.get().test().assertResult(); + } + + @Test + public void cancellingWindowCancelsUpstreamExactTimeAndSize() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(10, TimeUnit.MINUTES, 100) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstreamExactTimeAndSize() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(10, TimeUnit.MINUTES, 100) + .take(1) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + to + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + inner.get().test().assertResult(); + } + + @Test + public void cancellingWindowCancelsUpstreamExactTimeSkip() { + PublishSubject ps = PublishSubject.create(); + + TestObserver to = ps.window(10, 15, TimeUnit.MINUTES) + .take(1) + .flatMap(new Function, Observable>() { + @Override + public Observable apply(Observable w) throws Throwable { + return w.take(1); + } + }) + .test(); + + assertTrue(ps.hasObservers()); + + ps.onNext(1); + + to + .assertResult(1); + + assertFalse("Subject still has observers!", ps.hasObservers()); + } + + @Test + public void windowAbandonmentCancelsUpstreamExactTimeSkip() { + PublishSubject ps = PublishSubject.create(); + + final AtomicReference> inner = new AtomicReference<>(); + + TestObserver> to = ps.window(10, 15, TimeUnit.MINUTES) + .take(1) + .doOnNext(new Consumer>() { + @Override + public void accept(Observable v) throws Throwable { + inner.set(v); + } + }) + .test(); + + assertFalse("Subject still has observers!", ps.hasObservers()); + + to + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + + inner.get().test().assertResult(); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservable(o -> o.window(1, TimeUnit.SECONDS)); + } + + @Test + public void timedBoundarySignalAndDisposeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestScheduler scheduler = new TestScheduler(); + + PublishSubject ps = PublishSubject.create(); + + TestObserver> to = ps.window(1, TimeUnit.MINUTES, scheduler, 1) + .test(); + + TestHelper.race( + () -> ps.onNext(1), + () -> to.dispose() + ); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromTest.java index f87491e0e0..cfe093ed6b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableWithLatestFromTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,13 +19,13 @@ import java.util.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import org.mockito.InOrder; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -91,7 +91,7 @@ public void emptySource() { Observable result = source.withLatestFrom(other, COMBINER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); result.subscribe(to); @@ -117,7 +117,7 @@ public void emptyOther() { Observable result = source.withLatestFrom(other, COMBINER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); result.subscribe(to); @@ -143,7 +143,7 @@ public void unsubscription() { Observable result = source.withLatestFrom(other, COMBINER); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); result.subscribe(to); @@ -170,7 +170,7 @@ public void sourceThrows() { Observable result = source.withLatestFrom(other, COMBINER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); result.subscribe(to); @@ -198,7 +198,7 @@ public void otherThrows() { Observable result = source.withLatestFrom(other, COMBINER); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); result.subscribe(to); @@ -226,7 +226,7 @@ public void functionThrows() { Observable result = source.withLatestFrom(other, COMBINER_ERROR); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); result.subscribe(to); @@ -252,7 +252,7 @@ public void noDownstreamUnsubscribe() { Observable result = source.withLatestFrom(other, COMBINER); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); result.subscribe(to); @@ -276,7 +276,7 @@ public void manySources() { PublishSubject ps3 = PublishSubject.create(); PublishSubject main = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); main.withLatestFrom(new Observable[] { ps1, ps2, ps3 }, toArray) .subscribe(to); @@ -323,7 +323,7 @@ public void manySourcesIterable() { PublishSubject ps3 = PublishSubject.create(); PublishSubject main = PublishSubject.create(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); main.withLatestFrom(Arrays.>asList(ps1, ps2, ps3), toArray) .subscribe(to); @@ -368,8 +368,8 @@ public void manySourcesIterableSweep() { for (String val : new String[] { "1" /*, null*/ }) { int n = 35; for (int i = 0; i < n; i++) { - List> sources = new ArrayList>(); - List expected = new ArrayList(); + List> sources = new ArrayList<>(); + List expected = new ArrayList<>(); expected.add(val); for (int j = 0; j < i; j++) { @@ -377,7 +377,7 @@ public void manySourcesIterableSweep() { expected.add(String.valueOf(val)); } - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); PublishSubject main = PublishSubject.create(); @@ -397,7 +397,7 @@ public void manySourcesIterableSweep() { @Test public void withEmpty() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 3).withLatestFrom( new Observable[] { Observable.just(1), Observable.empty() }, toArray) @@ -410,7 +410,7 @@ public void withEmpty() { @Test public void withError() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.range(1, 3).withLatestFrom( new Observable[] { Observable.just(1), Observable.error(new TestException()) }, toArray) @@ -423,7 +423,7 @@ public void withError() { @Test public void withMainError() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.error(new TestException()).withLatestFrom( new Observable[] { Observable.just(1), Observable.just(1) }, toArray) @@ -438,7 +438,7 @@ public void withMainError() { public void with2Others() { Observable just = Observable.just(1); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); just.withLatestFrom(just, just, new Function3>() { @Override @@ -457,7 +457,7 @@ public List apply(Integer a, Integer b, Integer c) { public void with3Others() { Observable just = Observable.just(1); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); just.withLatestFrom(just, just, just, new Function4>() { @Override @@ -476,7 +476,7 @@ public List apply(Integer a, Integer b, Integer c, Integer d) { public void with4Others() { Observable just = Observable.just(1); - TestObserver> to = new TestObserver>(); + TestObserver> to = new TestObserver<>(); just.withLatestFrom(just, just, just, just, new Function5>() { @Override @@ -511,7 +511,7 @@ public Object apply(Integer a, Integer b, Integer c) throws Exception { @Test public void manyIteratorThrows() { Observable.just(1) - .withLatestFrom(new CrashingMappedIterable>(1, 100, 100, new Function>() { + .withLatestFrom(new CrashingMappedIterable<>(1, 100, 100, new Function>() { @Override public Observable apply(Integer v) throws Exception { return Observable.just(2); @@ -545,7 +545,7 @@ public void manyErrors() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new TestException("First")); observer.onNext(1); observer.onError(new TestException("Second")); @@ -579,7 +579,6 @@ public Object apply(Integer a, Integer b) throws Exception { .assertFailure(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void combineToNull2() { Observable.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipCompletionTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipCompletionTest.java index 76d14495db..89dde2cdc5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipCompletionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipCompletionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterableTest.java index 53fd62d50d..ce4ee90939 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,13 +20,13 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import org.mockito.InOrder; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; @@ -409,7 +409,7 @@ public void badSource() { new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onComplete(); observer.onNext(2); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipTest.java index e185dea68d..faebee3fa5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/observable/ObservableZipTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -350,7 +350,7 @@ public void aggregatorUnsubscribe() { PublishSubject r2 = PublishSubject.create(); /* define an Observer to receive aggregated events */ Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); Observable.zip(r1, r2, zipr2).subscribe(to); @@ -624,7 +624,7 @@ private static class TestObservable implements ObservableSource { public void subscribe(Observer observer) { // just store the variable where it can be accessed so we can manually trigger it this.observer = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); } } @@ -768,7 +768,7 @@ public String apply(Integer a, Integer b) { } }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); os.subscribe(new Consumer() { @Override @@ -795,7 +795,7 @@ public String apply(Integer a, Integer b) { } }).take(5); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); os.subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); @@ -820,7 +820,7 @@ public String apply(Integer a, Integer b) { } }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); os.subscribe(new DefaultObserver() { @Override @@ -884,7 +884,7 @@ public String apply(Notification t1, Notification t2) { }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); o.subscribe(new Consumer() { @Override @@ -913,7 +913,7 @@ public String apply(Integer t1, String t2) { }); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); o.subscribe(new Consumer() { @Override @@ -940,7 +940,7 @@ public Object apply(final Object[] args) { } }); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); o.subscribe(to); to.awaitDone(200, TimeUnit.MILLISECONDS); to.assertNoValues(); @@ -974,7 +974,7 @@ public void downstreamBackpressureRequestsWithFiniteSyncObservables() { Observable o1 = createInfiniteObservable(generatedA).take(Observable.bufferSize() * 2); Observable o2 = createInfiniteObservable(generatedB).take(Observable.bufferSize() * 2); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.zip(o1, o2, new BiFunction() { @Override @@ -1024,7 +1024,7 @@ Observable OBSERVABLE_OF_5_INTEGERS(final AtomicInteger numEmitted) { @Override public void subscribe(final Observer o) { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); o.onSubscribe(d); for (int i = 1; i <= 5; i++) { if (d.isDisposed()) { @@ -1045,7 +1045,7 @@ Observable ASYNC_OBSERVABLE_OF_INFINITE_INTEGERS(final CountDownLatch l @Override public void subscribe(final Observer o) { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); o.onSubscribe(d); Thread t = new Thread(new Runnable() { @@ -1089,7 +1089,7 @@ public Integer apply(Integer i1, Integer i2) { return i1 + i2; } }); - List expected = new ArrayList(); + List expected = new ArrayList<>(); for (int i = 0; i < 1026; i++) { expected.add(i * 3); } @@ -1299,7 +1299,6 @@ public Object apply(Integer a, Integer b) throws Exception { .assertFailure(TestException.class, "929"); } - @SuppressWarnings("unchecked") @Test public void zipArrayEmpty() { assertSame(Observable.empty(), Observable.zipArray(Functions.identity(), false, 16)); @@ -1404,7 +1403,7 @@ public Integer apply(Integer t1, Integer t2) throws Exception { public void firstErrorPreventsSecondSubscription() { final AtomicInteger counter = new AtomicInteger(); - List> observableList = new ArrayList>(); + List> observableList = new ArrayList<>(); observableList.add(Observable.create(new ObservableOnSubscribe() { @Override public void subscribe(ObservableEmitter e) @@ -1430,7 +1429,6 @@ public Object apply(Object[] a) throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void observableSourcesInIterable() { ObservableSource source = new ObservableSource() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleAmbTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleAmbTest.java index a6caec6171..8086aa7c1c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleAmbTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleAmbTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -71,7 +71,6 @@ public void ambWithSecondFires() { to.assertResult(2); } - @SuppressWarnings("unchecked") @Test public void ambIterableWithFirstFires() { PublishProcessor pp1 = PublishProcessor.create(); @@ -93,7 +92,6 @@ public void ambIterableWithFirstFires() { } - @SuppressWarnings("unchecked") @Test public void ambIterableWithSecondFires() { PublishProcessor pp1 = PublishProcessor.create(); @@ -114,7 +112,6 @@ public void ambIterableWithSecondFires() { to.assertResult(2); } - @SuppressWarnings("unchecked") @Test public void ambArrayEmpty() { Single.ambArray() @@ -122,13 +119,11 @@ public void ambArrayEmpty() { .assertFailure(NoSuchElementException.class); } - @SuppressWarnings("unchecked") @Test public void ambSingleSource() { assertSame(Single.never(), Single.ambArray(Single.never())); } - @SuppressWarnings("unchecked") @Test public void error() { Single.ambArray(Single.error(new TestException()), Single.just(1)) @@ -146,7 +141,6 @@ public void nullSourceSuccessRace() { final Subject ps = ReplaySubject.create(); ps.onNext(1); - @SuppressWarnings("unchecked") final Single source = Single.ambArray(ps.singleOrError(), Single.never(), Single.never(), null); Runnable r1 = new Runnable() { @@ -174,7 +168,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void multipleErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -214,7 +207,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void successErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -257,7 +249,8 @@ public void run() { @Test public void manySources() { - Single[] sources = new Single[32]; + @SuppressWarnings("unchecked") + Single[] sources = new Single[32]; Arrays.fill(sources, Single.never()); sources[31] = Single.just(31); @@ -272,21 +265,18 @@ public void ambWithOrder() { Single.just(1).ambWith(error).test().assertValue(1); } - @SuppressWarnings("unchecked") @Test public void ambIterableOrder() { Single error = Single.error(new RuntimeException()); Single.amb(Arrays.asList(Single.just(1), error)).test().assertValue(1); } - @SuppressWarnings("unchecked") @Test public void ambArrayOrder() { Single error = Single.error(new RuntimeException()); Single.ambArray(Single.just(1), error).test().assertValue(1); } - @SuppressWarnings("unchecked") @Test public void noWinnerSuccessDispose() throws Exception { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { @@ -314,7 +304,6 @@ public void accept(Object v, Throwable e) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void noWinnerErrorDispose() throws Exception { final TestException ex = new TestException(); @@ -343,7 +332,6 @@ public void accept(Object v, Throwable e) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void singleSourcesInIterable() { SingleSource source = new SingleSource() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleBlockingSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleBlockingSubscribeTest.java new file mode 100644 index 0000000000..a29c10975f --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleBlockingSubscribeTest.java @@ -0,0 +1,343 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleBlockingSubscribeTest { + + @Test + public void noArgSuccess() { + Single.just(1) + .blockingSubscribe(); + } + + @Test + public void noArgSuccessAsync() { + Single.just(1) + .delay(100, TimeUnit.MILLISECONDS) + .blockingSubscribe(); + } + + @Test + public void noArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new TestException()) + .blockingSubscribe(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void noArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new TestException()) + .delay(100, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void oneArgSuccess() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Single.just(1) + .blockingSubscribe(success); + + verify(success).accept(1); + } + + @Test + public void oneArgSuccessAsync() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Single.just(1) + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(success); + + verify(success).accept(1); + } + + @Test + public void oneArgSuccessFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + doThrow(new TestException()).when(success).accept(any()); + + Single.just(1) + .blockingSubscribe(success); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success).accept(1); + }); + } + + @Test + public void oneArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Single.error(new TestException()) + .blockingSubscribe(success); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + }); + } + + @Test + public void oneArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + + Single.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(success); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + }); + } + + @Test + public void twoArgSuccess() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Single.just(1) + .blockingSubscribe(success, consumer); + + verify(success).accept(1); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgSuccessAsync() throws Throwable { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Single.just(1) + .delay(50, TimeUnit.MILLISECONDS) + .blockingSubscribe(success, consumer); + + verify(success).accept(any()); + verify(consumer, never()).accept(any()); + } + + @Test + public void twoArgSuccessFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + doThrow(new TestException()).when(success).accept(any()); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Single.just(1) + .blockingSubscribe(success, consumer); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success).accept(any()); + verify(consumer, never()).accept(any()); + }); + } + + @Test + public void twoArgError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Single.error(new TestException()) + .blockingSubscribe(success, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgErrorAsync() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Single.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(success, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgErrorFails() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + doThrow(new TestException()).when(consumer).accept(any()); + + Single.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(success, consumer); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + + verify(success, never()).accept(any()); + verify(consumer).accept(any(TestException.class)); + }); + } + + @Test + public void twoArgInterrupted() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action onDispose = mock(Action.class); + + @SuppressWarnings("unchecked") + Consumer success = mock(Consumer.class); + @SuppressWarnings("unchecked") + Consumer consumer = mock(Consumer.class); + + Thread.currentThread().interrupt(); + + Single.never() + .doOnDispose(onDispose) + .blockingSubscribe(success, consumer); + + assertTrue("" + errors, errors.isEmpty()); + + verify(onDispose).run(); + verify(success, never()).accept(any()); + verify(consumer).accept(any(InterruptedException.class)); + }); + } + + @Test + public void observerSuccess() { + TestObserver to = new TestObserver<>(); + + Single.just(1) + .blockingSubscribe(to); + + to.assertResult(1); + } + + @Test + public void observerSuccessAsync() { + TestObserver to = new TestObserver<>(); + + Single.just(1) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(to); + + to.assertResult(1); + } + + @Test + public void observerError() { + TestObserver to = new TestObserver<>(); + + Single.error(new TestException()) + .blockingSubscribe(to); + + to.assertFailure(TestException.class); + } + + @Test + public void observerErrorAsync() { + TestObserver to = new TestObserver<>(); + + Single.error(new TestException()) + .delay(50, TimeUnit.MILLISECONDS, Schedulers.computation(), true) + .blockingSubscribe(to); + + to.assertFailure(TestException.class); + } + + @Test + public void observerDispose() throws Throwable { + Action onDispose = mock(Action.class); + + TestObserver to = new TestObserver<>(); + to.dispose(); + + Single.never() + .doOnDispose(onDispose) + .blockingSubscribe(to); + + to.assertEmpty(); + + verify(onDispose).run(); + } + + @Test + public void ovserverInterrupted() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Action onDispose = mock(Action.class); + + TestObserver to = new TestObserver<>(); + + Thread.currentThread().interrupt(); + + Single.never() + .doOnDispose(onDispose) + .blockingSubscribe(to); + + assertTrue("" + errors, errors.isEmpty()); + + verify(onDispose).run(); + to.assertFailure(InterruptedException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCacheTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCacheTest.java index 28eb94110b..ca46a92f12 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCacheTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatArrayDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatArrayDelayErrorTest.java new file mode 100644 index 0000000000..00ff0d5ad1 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatArrayDelayErrorTest.java @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.exceptions.TestException; + +public class SingleConcatArrayDelayErrorTest { + + @Test + public void normal() { + Single.concatArrayDelayError( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + ) + .test() + .assertFailure(TestException.class, 1, 2); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatArrayEagerDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatArrayEagerDelayErrorTest.java new file mode 100644 index 0000000000..bfd96ad3a9 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatArrayEagerDelayErrorTest.java @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.exceptions.TestException; + +public class SingleConcatArrayEagerDelayErrorTest { + + @Test + public void normal() { + Single.concatArrayEagerDelayError( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + ) + .test() + .assertFailure(TestException.class, 1, 2); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatDelayErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatDelayErrorTest.java new file mode 100644 index 0000000000..18dc2c83d2 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatDelayErrorTest.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.Arrays; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class SingleConcatDelayErrorTest { + + @Test + public void normalIterable() { + Single.concatDelayError(Arrays.asList( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + )) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void normalPublisher() { + Single.concatDelayError(Flowable.fromArray( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + )) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void normalPublisherPrefetch() { + Single.concatDelayError(Flowable.fromArray( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1, 2); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatEagerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatEagerTest.java new file mode 100644 index 0000000000..892ed8dd4d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatEagerTest.java @@ -0,0 +1,141 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.Arrays; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class SingleConcatEagerTest { + + @Test + public void iterableNormal() { + Single.concatEager(Arrays.asList( + Single.just(1), + Single.just(2) + )) + .test() + .assertResult(1, 2); + } + + @Test + public void iterableNormalMaxConcurrency() { + Single.concatEager(Arrays.asList( + Single.just(1), + Single.just(2) + ), 1) + .test() + .assertResult(1, 2); + } + + @Test + public void iterableError() { + Single.concatEager(Arrays.asList( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + )) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void iterableErrorMaxConcurrency() { + Single.concatEager(Arrays.asList( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void publisherNormal() { + Single.concatEager(Flowable.fromArray( + Single.just(1), + Single.just(2) + )) + .test() + .assertResult(1, 2); + } + + @Test + public void publisherNormalMaxConcurrency() { + Single.concatEager(Flowable.fromArray( + Single.just(1), + Single.just(2) + ), 1) + .test() + .assertResult(1, 2); + } + + @Test + public void publisherError() { + Single.concatEager(Flowable.fromArray( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + )) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void iterableDelayError() { + Single.concatEagerDelayError(Arrays.asList( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + )) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void iterableDelayErrorMaxConcurrency() { + Single.concatEagerDelayError(Arrays.asList( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void publisherDelayError() { + Single.concatEagerDelayError(Flowable.fromArray( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + )) + .test() + .assertFailure(TestException.class, 1, 2); + } + + @Test + public void publisherDelayErrorMaxConcurrency() { + Single.concatEagerDelayError(Flowable.fromArray( + Single.just(1), + Single.error(new TestException()), + Single.just(2) + ), 1) + .test() + .assertFailure(TestException.class, 1, 2); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapCompletableTest.java new file mode 100644 index 0000000000..3c603b26b9 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapCompletableTest.java @@ -0,0 +1,114 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleConcatMapCompletableTest extends RxJavaTest { + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.just(1).concatMapCompletable(new Function() { + @Override + public Completable apply(Integer v) throws Exception { + return Completable.complete(); + } + })); + } + + @Test + public void normal() { + final boolean[] b = { false }; + + Single.just(1) + .concatMapCompletable(new Function() { + @Override + public Completable apply(Integer t) throws Exception { + return Completable.complete().doOnComplete(new Action() { + @Override + public void run() throws Exception { + b[0] = true; + } + }); + } + }) + .test() + .assertResult(); + + assertTrue(b[0]); + } + + @Test + public void error() { + final boolean[] b = { false }; + + Single.error(new TestException()) + .concatMapCompletable(new Function() { + @Override + public Completable apply(Integer t) throws Exception { + return Completable.complete().doOnComplete(new Action() { + @Override + public void run() throws Exception { + b[0] = true; + } + }); + } + }) + .test() + .assertFailure(TestException.class); + + assertFalse(b[0]); + } + + @Test + public void mapperThrows() { + final boolean[] b = { false }; + + Single.just(1) + .concatMapCompletable(new Function() { + @Override + public Completable apply(Integer t) throws Exception { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + + assertFalse(b[0]); + } + + @Test + public void mapperReturnsNull() { + final boolean[] b = { false }; + + Single.just(1) + .concatMapCompletable(new Function() { + @Override + public Completable apply(Integer t) throws Exception { + return null; + } + }) + .test() + .assertFailure(NullPointerException.class); + + assertFalse(b[0]); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapMaybeTest.java new file mode 100644 index 0000000000..24710d824a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapMaybeTest.java @@ -0,0 +1,141 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleConcatMapMaybeTest extends RxJavaTest { + @Test + public void concatMapMaybeValue() { + Single.just(1).concatMapMaybe(new Function>() { + @Override public MaybeSource apply(final Integer integer) throws Exception { + if (integer == 1) { + return Maybe.just(2); + } + + return Maybe.just(1); + } + }) + .test() + .assertResult(2); + } + + @Test + public void concatMapMaybeValueDifferentType() { + Single.just(1).concatMapMaybe(new Function>() { + @Override public MaybeSource apply(final Integer integer) throws Exception { + if (integer == 1) { + return Maybe.just("2"); + } + + return Maybe.just("1"); + } + }) + .test() + .assertResult("2"); + } + + @Test + public void concatMapMaybeValueNull() { + Single.just(1).concatMapMaybe(new Function>() { + @Override public MaybeSource apply(final Integer integer) throws Exception { + return null; + } + }) + .to(TestHelper.testConsumer()) + .assertNoValues() + .assertError(NullPointerException.class) + .assertErrorMessage("The mapper returned a null MaybeSource"); + } + + @Test + public void concatMapMaybeValueErrorThrown() { + Single.just(1).concatMapMaybe(new Function>() { + @Override public MaybeSource apply(final Integer integer) throws Exception { + throw new RuntimeException("something went terribly wrong!"); + } + }) + .to(TestHelper.testConsumer()) + .assertNoValues() + .assertError(RuntimeException.class) + .assertErrorMessage("something went terribly wrong!"); + } + + @Test + public void concatMapMaybeError() { + RuntimeException exception = new RuntimeException("test"); + + Single.error(exception).concatMapMaybe(new Function>() { + @Override public MaybeSource apply(final Object integer) throws Exception { + return Maybe.just(new Object()); + } + }) + .test() + .assertError(exception); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.just(1).concatMapMaybe(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.just(1); + } + })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToMaybe(new Function, MaybeSource>() { + @Override + public MaybeSource apply(Single v) throws Exception { + return v.concatMapMaybe(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.just(1); + } + }); + } + }); + } + + @Test + public void mapsToError() { + Single.just(1).concatMapMaybe(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.error(new TestException()); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapsToEmpty() { + Single.just(1).concatMapMaybe(new Function>() { + @Override + public MaybeSource apply(Integer v) throws Exception { + return Maybe.empty(); + } + }) + .test() + .assertResult(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapTest.java new file mode 100644 index 0000000000..1fa2ebaf0b --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatMapTest.java @@ -0,0 +1,132 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleConcatMapTest extends RxJavaTest { + + @Test + public void concatMapValue() { + Single.just(1).concatMap(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + if (integer == 1) { + return Single.just(2); + } + + return Single.just(1); + } + }) + .test() + .assertResult(2); + } + + @Test + public void concatMapValueDifferentType() { + Single.just(1).concatMap(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + if (integer == 1) { + return Single.just("2"); + } + + return Single.just("1"); + } + }) + .test() + .assertResult("2"); + } + + @Test + public void concatMapValueNull() { + Single.just(1).concatMap(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + return null; + } + }) + .to(TestHelper.testConsumer()) + .assertNoValues() + .assertError(NullPointerException.class) + .assertErrorMessage("The single returned by the mapper is null"); + } + + @Test + public void concatMapValueErrorThrown() { + Single.just(1).concatMap(new Function>() { + @Override public SingleSource apply(final Integer integer) throws Exception { + throw new RuntimeException("something went terribly wrong!"); + } + }) + .to(TestHelper.testConsumer()) + .assertNoValues() + .assertError(RuntimeException.class) + .assertErrorMessage("something went terribly wrong!"); + } + + @Test + public void concatMapError() { + RuntimeException exception = new RuntimeException("test"); + + Single.error(exception).concatMap(new Function>() { + @Override public SingleSource apply(final Object integer) throws Exception { + return Single.just(new Object()); + } + }) + .test() + .assertError(exception); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.just(1).concatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + return Single.just(2); + } + })); + } + + @Test + public void mappedSingleOnError() { + Single.just(1).concatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + return Single.error(new TestException()); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingle(new Function, SingleSource>() { + @Override + public SingleSource apply(Single s) + throws Exception { + return s.concatMap(new Function>() { + @Override + public SingleSource apply(Object v) + throws Exception { + return Single.just(v); + } + }); + } + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatPublisherTest.java index bd31e28430..883903e1d9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.operators.single; import java.util.concurrent.Callable; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatTest.java index eebe0c1020..fb94277e43 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleConcatTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -70,7 +70,6 @@ public void concatArray() { } } - @SuppressWarnings("unchecked") @Test public void concatArrayEagerTest() { PublishProcessor pp1 = PublishProcessor.create(); @@ -89,7 +88,6 @@ public void concatArrayEagerTest() { ts.assertComplete(); } - @SuppressWarnings("unchecked") @Test public void concatEagerIterableTest() { PublishProcessor pp1 = PublishProcessor.create(); @@ -143,7 +141,6 @@ public void concatObservable() { } } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscription() { final int[] calls = { 0 }; @@ -163,7 +160,6 @@ public void subscribe(SingleEmitter s) throws Exception { assertEquals(1, calls[0]); } - @SuppressWarnings("unchecked") @Test public void noSubsequentSubscriptionIterable() { final int[] calls = { 0 }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleContainstTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleContainstTest.java index 4a991c4cbf..2f5baa4ed0 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleContainstTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleContainstTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java index 98c0a67903..862757560a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleCreateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,18 +25,14 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Cancellable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class SingleCreateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void nullArgument() { - Single.create(null); - } - @Test + @SuppressUndeliverable public void basic() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Single.create(new SingleOnSubscribe() { @Override @@ -56,9 +52,10 @@ public void subscribe(SingleEmitter e) throws Exception { } @Test + @SuppressUndeliverable public void basicWithCancellable() { - final Disposable d1 = Disposables.empty(); - final Disposable d2 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); + final Disposable d2 = Disposable.empty(); Single.create(new SingleOnSubscribe() { @Override @@ -85,8 +82,9 @@ public void cancel() throws Exception { } @Test + @SuppressUndeliverable public void basicWithError() { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Single.create(new SingleOnSubscribe() { @Override @@ -193,7 +191,7 @@ public void createConsumerThrowsResource() { Single.create(new SingleOnSubscribe() { @Override public void subscribe(SingleEmitter s) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); s.setDisposable(d); try { s.onSuccess(1); @@ -257,7 +255,7 @@ public void createConsumerThrowsResourceOnError() { Single.create(new SingleOnSubscribe() { @Override public void subscribe(SingleEmitter s) throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); s.setDisposable(d); try { s.onError(new IOException()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDeferTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDeferTest.java index 962a3694c2..7fed907ab4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDeferTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDeferTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java index 4f97e5c6bf..bc137d1612 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDelayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,14 +23,14 @@ import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.TestHelper; public class SingleDelayTest extends RxJavaTest { @@ -138,7 +138,7 @@ public void delaySubscriptionTimeCustomScheduler() throws Exception { @Test public void onErrorCalledOnScheduler() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference thread = new AtomicReference(); + final AtomicReference thread = new AtomicReference<>(); Single.error(new Exception()) .delay(0, TimeUnit.MILLISECONDS, Schedulers.newThread()) @@ -215,7 +215,7 @@ public void withObservableError2() { .delaySubscription(new Observable() { @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(1); observer.onError(new TestException()); } @@ -270,4 +270,16 @@ public Single apply(Single s) throws Exception { }); } + + @Test + public void withPublisherDoubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowableToSingle( + f -> SingleSubject.create().delaySubscription(f)); + } + + @Test + public void withObservableDoubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeObservableToSingle( + o -> SingleSubject.create().delaySubscription(o)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerializeTest.java index 5bf1409498..6cd05b7128 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDematerializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDetachTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDetachTest.java index 1b7f5e7a34..17812c8957 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDetachTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDetachTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -64,8 +64,8 @@ public void onSuccess() { @Test public void cancelDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Single() { @Override @@ -90,8 +90,8 @@ protected void subscribeActual(SingleObserver observer) { @Test public void errorDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Single() { @Override @@ -116,8 +116,8 @@ protected void subscribeActual(SingleObserver observer) { @Test public void successDetaches() throws Exception { - Disposable d = Disposables.empty(); - final WeakReference wr = new WeakReference(d); + Disposable d = Disposable.empty(); + final WeakReference wr = new WeakReference<>(d); TestObserver to = new Single() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccessTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccessTest.java index 679eac1e64..de83cecb1a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccessTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterSuccessTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class SingleDoAfterSuccessTest extends RxJavaTest { - final List values = new ArrayList(); + final List values = new ArrayList<>(); final Consumer afterSuccess = new Consumer() { @Override @@ -67,11 +67,6 @@ public void error() { assertTrue(values.isEmpty()); } - @Test(expected = NullPointerException.class) - public void consumerNull() { - Single.just(1).doAfterSuccess(null); - } - @Test public void justConditional() { Single.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminateTest.java index 3096263c88..6b98da8c66 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoAfterTerminateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public void run() throws Exception { } }; - private final TestObserver to = new TestObserver(); + private final TestObserver to = new TestObserver<>(); @Test public void just() { @@ -61,11 +61,6 @@ public void error() { assertAfterTerminateCalledOnce(); } - @Test(expected = NullPointerException.class) - public void afterTerminateActionNull() { - Single.just(1).doAfterTerminate(null); - } - @Test public void justConditional() { Single.just(1) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinallyTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinallyTest.java index 22f6e45c29..64afc620ac 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinallyTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoFinallyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -65,11 +65,6 @@ public Single apply(Single f) throws Exception { }); } - @Test(expected = NullPointerException.class) - public void nullAction() { - Single.just(1).doFinally(null); - } - @Test public void actionThrows() { List errors = TestHelper.trackPluginErrors(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnLifecycleTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnLifecycleTest.java new file mode 100644 index 0000000000..f14d03cc8d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnLifecycleTest.java @@ -0,0 +1,152 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleDoOnLifecycleTest extends RxJavaTest { + + @Test + public void success() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Single.just(1) + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertResult(1); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void error() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + Single.error(new TestException()) + .doOnLifecycle(onSubscribe, onDispose) + .test() + .assertFailure(TestException.class); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + } + + @Test + public void onSubscribeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + doThrow(new TestException("First")).when(onSubscribe).accept(any()); + + Disposable bs = Disposable.empty(); + + new Single() { + @Override + protected void subscribeActual(SingleObserver observer) { + observer.onSubscribe(bs); + observer.onError(new TestException("Second")); + observer.onSuccess(1); + } + } + .doOnLifecycle(onSubscribe, onDispose) + .to(TestHelper.testConsumer()) + .assertFailureAndMessage(TestException.class, "First"); + + assertTrue(bs.isDisposed()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "Second"); + + verify(onSubscribe).accept(any()); + verify(onDispose, never()).run(); + }); + } + + @Test + public void onDisposeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + doThrow(new TestException("First")).when(onDispose).run(); + + SingleSubject ss = SingleSubject.create(); + + TestObserver to = ss + .doOnLifecycle(onSubscribe, onDispose) + .test(); + + assertTrue(ss.hasObservers()); + + to.dispose(); + + assertFalse(ss.hasObservers()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class, "First"); + + verify(onSubscribe).accept(any()); + verify(onDispose).run(); + }); + } + + @Test + public void dispose() throws Throwable { + @SuppressWarnings("unchecked") + Consumer onSubscribe = mock(Consumer.class); + Action onDispose = mock(Action.class); + + SingleSubject ss = SingleSubject.create(); + + TestObserver to = ss + .doOnLifecycle(onSubscribe, onDispose) + .test(); + + assertTrue(ss.hasObservers()); + + to.dispose(); + + assertFalse(ss.hasObservers()); + + verify(onSubscribe).accept(any()); + verify(onDispose).run(); + } + + @Test + public void isDisposed() { + TestHelper.checkDisposed(SingleSubject.create().doOnLifecycle(d -> { }, () -> { })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingle(m -> m.doOnLifecycle(d -> { }, () -> { })); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminateTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminateTest.java index 58ba686e81..814a38bded 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminateTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTerminateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,11 +27,6 @@ public class SingleDoOnTerminateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void doOnTerminate() { - Single.just(1).doOnTerminate(null); - } - @Test public void doOnTerminateSuccess() { final AtomicBoolean atomicBoolean = new AtomicBoolean(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTest.java index 9bea6e178c..92ce91b65a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleDoOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -332,7 +332,7 @@ public void accept(Integer v) throws Exception { public void onSubscribeCrash() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable bs = Disposables.empty(); + final Disposable bs = Disposable.empty(); new Single() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java index c664784869..9526299231 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleEqualsTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,11 +24,39 @@ public class SingleEqualsTest extends RxJavaTest { + @Test + public void bothSucceedEqual() { + Single.sequenceEqual(Single.just(1), Single.just(1)) + .test() + .assertResult(true); + } + + @Test + public void bothSucceedNotEqual() { + Single.sequenceEqual(Single.just(1), Single.just(2)) + .test() + .assertResult(false); + } + + @Test + public void firstSucceedOtherError() { + Single.sequenceEqual(Single.just(1), Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void firstErrorOtherSucceed() { + Single.sequenceEqual(Single.error(new TestException()), Single.just(1)) + .test() + .assertFailure(TestException.class); + } + @Test public void bothError() { List errors = TestHelper.trackPluginErrors(); try { - Single.equals(Single.error(new TestException("One")), Single.error(new TestException("Two"))) + Single.sequenceEqual(Single.error(new TestException("One")), Single.error(new TestException("Two"))) .to(TestHelper.testConsumer()) .assertFailureAndMessage(TestException.class, "One"); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleErrorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleErrorTest.java index b3619b5992..835ad2ad09 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleErrorTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleErrorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapBiSelectorTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapBiSelectorTest.java new file mode 100644 index 0000000000..78705fe166 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapBiSelectorTest.java @@ -0,0 +1,205 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleFlatMapBiSelectorTest extends RxJavaTest { + + BiFunction stringCombine() { + return new BiFunction() { + @Override + public String apply(Integer a, Integer b) throws Exception { + return a + ":" + b; + } + }; + } + + @Test + public void normal() { + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + return Single.just(2); + } + }, stringCombine()) + .test() + .assertResult("1:2"); + } + + @Test + public void errorWithJust() { + final int[] call = { 0 }; + + Single.error(new TestException()) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + call[0]++; + return Single.just(1); + } + }, stringCombine()) + .test() + .assertFailure(TestException.class); + + assertEquals(0, call[0]); + } + + @Test + public void justWithError() { + final int[] call = { 0 }; + + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + call[0]++; + return Single.error(new TestException()); + } + }, stringCombine()) + .test() + .assertFailure(TestException.class); + + assertEquals(1, call[0]); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(SingleSubject.create() + .flatMap(new Function>() { + @Override + public SingleSource apply(Object v) throws Exception { + return Single.just(1); + } + }, new BiFunction() { + @Override + public Object apply(Object a, Integer b) throws Exception { + return b; + } + })); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingle(new Function, SingleSource>() { + @Override + public SingleSource apply(Single v) throws Exception { + return v.flatMap(new Function>() { + @Override + public SingleSource apply(Object v) throws Exception { + return Single.just(1); + } + }, new BiFunction() { + @Override + public Object apply(Object a, Integer b) throws Exception { + return b; + } + }); + } + }); + } + + @Test + public void mapperThrows() { + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + throw new TestException(); + } + }, stringCombine()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void mapperReturnsNull() { + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + return null; + } + }, stringCombine()) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void resultSelectorThrows() { + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + return Single.just(2); + } + }, new BiFunction() { + @Override + public Object apply(Integer a, Integer b) throws Exception { + throw new TestException(); + } + }) + .test() + .assertFailure(TestException.class); + } + + @Test + public void resultSelectorReturnsNull() { + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + return Single.just(2); + } + }, new BiFunction() { + @Override + public Object apply(Integer a, Integer b) throws Exception { + return null; + } + }) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void mapperCancels() { + final TestObserver to = new TestObserver<>(); + + Single.just(1) + .flatMap(new Function>() { + @Override + public SingleSource apply(Integer v) throws Exception { + to.dispose(); + return Single.just(2); + } + }, new BiFunction() { + @Override + public Integer apply(Integer a, Integer b) throws Exception { + throw new IllegalStateException(); + } + }) + .subscribeWith(to) + .assertEmpty(); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletableTest.java index 6e663394e5..c4af049aec 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapCompletableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java index 3fa9a2b462..277b9aa13b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,13 +21,15 @@ import org.junit.Test; import org.reactivestreams.Subscription; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -467,7 +469,7 @@ public void slowPathCancelAfterHasNext() { final Integer[] a = new Integer[1000]; Arrays.fill(a, 1); - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Single.just(1) .flattenAsFlowable(new Function>() { @@ -511,7 +513,7 @@ public void fastPathCancelAfterHasNext() { final Integer[] a = new Integer[1000]; Arrays.fill(a, 1); - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); Single.just(1) .flattenAsFlowable(new Function>() { @@ -585,4 +587,51 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToFlowable(s -> s.flattenAsFlowable(v -> Collections.emptyList())); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(SingleSubject.create().flattenAsFlowable(v -> Collections.emptyList())); + } + + @Test + public void slowPatchCancelAfterOnNext() { + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + cancel(); + onComplete(); + } + }; + + Single.just(1) + .flattenAsFlowable(v -> Arrays.asList(1, 2)) + .subscribe(ts); + + ts.assertResult(1); + } + + @Test + public void onSuccessRequestRace() { + List list = Arrays.asList(1); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + SingleSubject ss = SingleSubject.create(); + + TestSubscriber ts = ss.flattenAsFlowable(v -> list) + .test(0L); + + TestHelper.race( + () -> ss.onSuccess(1), + () -> ts.request(1) + ); + + ts.assertResult(1); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservableTest.java index 00fc69fdd9..82448746b8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapIterableObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,8 +25,9 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.util.CrashingIterable; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -86,7 +87,7 @@ public Iterable apply(Integer v) throws Exception { @Test public void fused() { - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); Single.just(1).flattenAsObservable(new Function>() { @Override @@ -104,7 +105,7 @@ public Iterable apply(Integer v) throws Exception { @Test public void fusedNoSync() { - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); Single.just(1).flattenAsObservable(new Function>() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybeTest.java index b98bf5537f..6081a621e8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapMaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,12 +21,6 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class SingleFlatMapMaybeTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void flatMapMaybeNull() { - Single.just(1) - .flatMapMaybe(null); - } - @Test public void flatMapMaybeValue() { Single.just(1).flatMapMaybe(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapNotificationTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapNotificationTest.java new file mode 100644 index 0000000000..00f1337b2c --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapNotificationTest.java @@ -0,0 +1,113 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.testsupport.*; + +public class SingleFlatMapNotificationTest extends RxJavaTest { + + @Test + public void dispose() { + TestHelper.checkDisposed(Single.just(1) + .flatMap(Functions.justFunction(Single.just(1)), + Functions.justFunction(Single.just(1)))); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingle(new Function, SingleSource>() { + @Override + public SingleSource apply(Single m) throws Exception { + return m + .flatMap(Functions.justFunction(Single.just(1)), + Functions.justFunction(Single.just(1))); + } + }); + } + + @Test + public void onSuccessNull() { + Single.just(1) + .flatMap(Functions.justFunction((Single)null), + Functions.justFunction(Single.just(1))) + .test() + .assertFailure(NullPointerException.class); + } + + @Test + public void onErrorNull() { + TestObserverEx to = Single.error(new TestException()) + .flatMap(Functions.justFunction(Single.just(1)), + Functions.justFunction((Single)null)) + .to(TestHelper.testConsumer()) + .assertFailure(CompositeException.class); + + List ce = TestHelper.compositeList(to.errors().get(0)); + + TestHelper.assertError(ce, 0, TestException.class); + TestHelper.assertError(ce, 1, NullPointerException.class); + } + + @Test + public void onSuccessError() { + Single.just(1) + .flatMap(Functions.justFunction(Single.error(new TestException())), + Functions.justFunction((Single)null)) + .test() + .assertFailure(TestException.class); + } + + @Test + public void onSucccessSuccess() { + Single.just(1) + .flatMap(v -> Single.just(2), e -> Single.just(3)) + .test() + .assertResult(2); + } + + @Test + public void onErrorSuccess() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new TestException()) + .flatMap(v -> Single.just(2), e -> Single.just(3)) + .test() + .assertResult(3); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void onErrorError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new TestException()) + .flatMap(v -> Single.just(2), e -> Single.error(new IOException())) + .test() + .assertFailure(IOException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapTest.java index 1845a4967d..08d3d24c5a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFlatMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -129,11 +129,6 @@ public Publisher apply(Integer v) throws Exception { .assertResult(1, 2, 3, 4, 5); } - @Test(expected = NullPointerException.class) - public void flatMapPublisherMapperNull() { - Single.just(1).flatMapPublisher(null); - } - @Test public void flatMapPublisherMapperThrows() { final TestException ex = new TestException(); @@ -215,12 +210,6 @@ public void run() throws Exception { ts.assertNotTerminated(); } - @Test(expected = NullPointerException.class) - public void flatMapNull() { - Single.just(1) - .flatMap(null); - } - @Test public void flatMapValue() { Single.just(1).flatMap(new Function>() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallableTest.java index b7c4ebf331..3352029989 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromCallableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -169,7 +169,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromCallableObservable .subscribeOn(Schedulers.computation()) @@ -230,7 +230,7 @@ public Object call() throws Exception { @Test public void disposedOnCall() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Single.fromCallable(new Callable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromMaybeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromMaybeTest.java new file mode 100644 index 0000000000..293cedf574 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromMaybeTest.java @@ -0,0 +1,72 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.*; + +import java.util.NoSuchElementException; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.MaybeSubject; + +public class SingleFromMaybeTest extends RxJavaTest { + + @Test + public void success() { + Single.fromMaybe(Maybe.just(1).hide()) + .test() + .assertResult(1); + } + + @Test + public void empty() { + Single.fromMaybe(Maybe.empty().hide()) + .test() + .assertFailure(NoSuchElementException.class); + } + + @Test + public void emptyDefault() { + Single.fromMaybe(Maybe.empty().hide(), 1) + .test() + .assertResult(1); + } + + @Test + public void error() { + Single.fromMaybe(Maybe.error(new TestException()).hide()) + .test() + .assertFailure(TestException.class); + } + + @Test + public void cancelComposes() { + MaybeSubject ms = MaybeSubject.create(); + + TestObserver to = Single.fromMaybe(ms) + .test(); + + to.assertEmpty(); + + assertTrue(ms.hasObservers()); + + to.dispose(); + + assertFalse(ms.hasObservers()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisherTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisherTest.java index 74261bc7dd..080870b228 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplierTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplierTest.java index 479999d8f3..927ec7f837 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplierTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromSupplierTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -170,7 +170,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { Observer observer = TestHelper.mockObserver(); - TestObserver outer = new TestObserver(observer); + TestObserver outer = new TestObserver<>(observer); fromSupplierObservable .subscribeOn(Schedulers.computation()) @@ -231,7 +231,7 @@ public Object get() throws Exception { @Test public void disposedOnCall() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Single.fromSupplier(new Supplier() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromTest.java index 46847bedd3..a322d8852e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleFromTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,8 @@ public class SingleFromTest extends RxJavaTest { @Test public void fromFuture() throws Exception { - Single.fromFuture(Flowable.just(1).toFuture(), Schedulers.io()) + Single.fromFuture(Flowable.just(1).toFuture()) + .subscribeOn(Schedulers.io()) .test() .awaitDone(5, TimeUnit.SECONDS) .assertResult(1); @@ -32,7 +33,8 @@ public void fromFuture() throws Exception { @Test public void fromFutureTimeout() throws Exception { - Single.fromFuture(Flowable.never().toFuture(), 1, TimeUnit.SECONDS, Schedulers.io()) + Single.fromFuture(Flowable.never().toFuture(), 1, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()) .test() .awaitDone(5, TimeUnit.SECONDS) .assertFailure(TimeoutException.class); diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleHideTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleHideTest.java index 9c5609b571..d13218bc84 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleHideTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleHideTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java index e551b882d1..478a03d9ff 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleInternalHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,8 +31,8 @@ public void utilityClass() { @Test public void noSuchElementCallableEnum() { - assertEquals(1, SingleInternalHelper.NoSuchElementCallable.values().length); - assertNotNull(SingleInternalHelper.NoSuchElementCallable.valueOf("INSTANCE")); + assertEquals(1, SingleInternalHelper.NoSuchElementSupplier.values().length); + assertNotNull(SingleInternalHelper.NoSuchElementSupplier.valueOf("INSTANCE")); } @Test @@ -41,12 +41,6 @@ public void toFlowableEnum() { assertNotNull(SingleInternalHelper.ToFlowable.valueOf("INSTANCE")); } - @Test - public void toObservableEnum() { - assertEquals(1, SingleInternalHelper.ToObservable.values().length); - assertNotNull(SingleInternalHelper.ToObservable.valueOf("INSTANCE")); - } - @Test public void singleIterableToFlowableIterable() { Iterable> it = SingleInternalHelper.iterableToFlowable( diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleLiftTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleLiftTest.java index 1638cb993f..6fdd626bd3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleLiftTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleLiftTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMapTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMapTest.java index 1ae83e9158..eb220e0580 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,11 +21,6 @@ public class SingleMapTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void mapNull() { - Single.just(1).map(null); - } - @Test public void mapValue() { Single.just(1).map(new Function() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterializeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterializeTest.java index 00b5f8d712..46625b80ab 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterializeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMaterializeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ public class SingleMaterializeTest extends RxJavaTest { @Test - @SuppressWarnings("unchecked") public void success() { Single.just(1) .materialize() @@ -33,7 +32,6 @@ public void success() { } @Test - @SuppressWarnings("unchecked") public void error() { TestException ex = new TestException(); Maybe.error(ex) diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeArrayTest.java new file mode 100644 index 0000000000..b2bda3a7d7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeArrayTest.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class SingleMergeArrayTest extends RxJavaTest { + + @Test + public void normal() { + Single.mergeArray(Single.just(1), Single.just(2), Single.just(3)) + .test() + .assertResult(1, 2, 3); + } + + @Test + public void error() { + Single.mergeArray(Single.just(1), Single.error(new TestException()), Single.just(3)) + .test() + .assertFailure(TestException.class, 1); + } + + @Test + public void normalDelayError() { + Single.mergeArrayDelayError(Single.just(1), Single.just(2), Single.just(3)) + .test() + .assertResult(1, 2, 3); + } + + @Test + public void errorDelayError() { + Single.mergeArrayDelayError(Single.just(1), Single.error(new TestException()), Single.just(3)) + .test() + .assertFailure(TestException.class, 1, 3); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeTest.java index ee991b431b..dd4844da5b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMergeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -72,7 +72,6 @@ public void mergeErrors() { } } - @SuppressWarnings("unchecked") @Test public void mergeDelayErrorIterable() { Single.mergeDelayError(Arrays.asList( diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMiscTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMiscTest.java index bf6e4fa8cf..93eebfe7ec 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMiscTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleMiscTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,10 +18,10 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.*; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -51,7 +51,7 @@ public void wrap() { Single.wrap(new SingleSource() { @Override public void subscribe(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(1); } }) @@ -280,11 +280,11 @@ public void toObservable() { @Test public void equals() { - Single.equals(Single.just(1), Single.just(1).hide()) + Single.sequenceEqual(Single.just(1), Single.just(1).hide()) .test() .assertResult(true); - Single.equals(Single.just(1), Single.just(2)) + Single.sequenceEqual(Single.just(1), Single.just(2)) .test() .assertResult(false); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOnTest.java index afb4494bef..4abc87e51f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleObserveOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOfTypeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOfTypeTest.java new file mode 100644 index 0000000000..9569e9a35d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOfTypeTest.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleOfTypeTest extends RxJavaTest { + + @Test + public void normal() { + Single.just(1).ofType(Integer.class) + .test() + .assertResult(1); + } + + @Test + public void normalDowncast() { + TestObserver to = Single.just(1) + .ofType(Number.class) + .test(); + // don't make this fluent, target type required! + to.assertResult((Number)1); + } + + @Test + public void notInstance() { + TestObserver to = Single.just(1) + .ofType(String.class) + .test(); + // don't make this fluent, target type required! + to.assertResult(); + } + + @Test + public void error() { + TestObserver to = Single.error(new TestException()) + .ofType(Number.class) + .test(); + // don't make this fluent, target type required! + to.assertFailure(TestException.class); + } + + @Test + public void errorNotInstance() { + TestObserver to = Single.error(new TestException()) + .ofType(String.class) + .test(); + // don't make this fluent, target type required! + to.assertFailure(TestException.class); + } + + @Test + public void dispose() { + TestHelper.checkDisposedSingleToMaybe(new Function, Maybe>() { + @Override + public Maybe apply(Single m) throws Exception { + return m.ofType(Object.class); + } + }); + } + + @Test + public void isDisposed() { + PublishProcessor pp = PublishProcessor.create(); + + TestHelper.checkDisposed(pp.singleElement().ofType(Object.class)); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToMaybe(new Function, Maybe>() { + @Override + public Maybe apply(Single f) throws Exception { + return f.ofType(Object.class); + } + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorCompleteTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorCompleteTest.java new file mode 100644 index 0000000000..9b184ae7f8 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorCompleteTest.java @@ -0,0 +1,113 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.*; + +import java.io.IOException; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.*; + +public class SingleOnErrorCompleteTest { + + @Test + public void normal() { + Single.just(1) + .onErrorComplete() + .test() + .assertResult(1); + } + + @Test + public void error() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new TestException()) + .onErrorComplete() + .test() + .assertResult(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorMatches() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new TestException()) + .onErrorComplete(error -> error instanceof TestException) + .test() + .assertResult(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorNotMatches() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Single.error(new IOException()) + .onErrorComplete(error -> error instanceof TestException) + .test() + .assertFailure(IOException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void errorPredicateCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestObserverEx to = Single.error(new IOException()) + .onErrorComplete(error -> { throw new TestException(); }) + .subscribeWith(new TestObserverEx<>()) + .assertFailure(CompositeException.class); + + TestHelper.assertError(to, 0, IOException.class); + TestHelper.assertError(to, 1, TestException.class); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void dispose() { + SingleSubject ss = SingleSubject.create(); + + TestObserver to = ss + .onErrorComplete() + .test(); + + assertTrue("No subscribers?!", ss.hasObservers()); + + to.dispose(); + + assertFalse("Still subscribers?!", ss.hasObservers()); + } + + @Test + public void onSubscribe() { + TestHelper.checkDoubleOnSubscribeSingleToMaybe(f -> f.onErrorComplete()); + } + + @Test + public void isDisposed() { + TestHelper.checkDisposed(SingleSubject.create().onErrorComplete()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorXTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorXTest.java index 990a0fdec6..42e36eaba8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorXTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleOnErrorXTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSafeSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSafeSubscribeTest.java new file mode 100644 index 0000000000..d9711c67c0 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSafeSubscribeTest.java @@ -0,0 +1,154 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import org.junit.Test; +import org.mockito.InOrder; + +import io.reactivex.rxjava3.annotations.NonNull; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleSafeSubscribeTest { + + @Test + public void normalSuccess() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + SingleObserver consumer = mock(SingleObserver.class); + + Single.just(1) + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onSuccess(1); + order.verifyNoMoreInteractions(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void normalError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + SingleObserver consumer = mock(SingleObserver.class); + + Single.error(new TestException()) + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onError(any(TestException.class)); + order.verifyNoMoreInteractions(); + + assertTrue("" + errors, errors.isEmpty()); + }); + } + + @Test + public void onSubscribeCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + SingleObserver consumer = mock(SingleObserver.class); + doThrow(new TestException()).when(consumer).onSubscribe(any()); + + Disposable d = Disposable.empty(); + + new Single() { + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + observer.onSubscribe(d); + // none of the following should arrive at the consumer + observer.onSuccess(1); + observer.onError(new IOException()); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verifyNoMoreInteractions(); + + assertTrue(d.isDisposed()); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + TestHelper.assertUndeliverable(errors, 1, IOException.class); + }); + } + + @Test + public void onSuccessCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + SingleObserver consumer = mock(SingleObserver.class); + doThrow(new TestException()).when(consumer).onSuccess(any()); + + new Single() { + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + observer.onSubscribe(Disposable.empty()); + observer.onSuccess(1); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onSuccess(1); + order.verifyNoMoreInteractions(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } + + @Test + public void onErrorCrash() throws Throwable { + TestHelper.withErrorTracking(errors -> { + @SuppressWarnings("unchecked") + SingleObserver consumer = mock(SingleObserver.class); + doThrow(new TestException()).when(consumer).onError(any()); + + new Single() { + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + observer.onSubscribe(Disposable.empty()); + // none of the following should arrive at the consumer + observer.onError(new IOException()); + } + } + .safeSubscribe(consumer); + + InOrder order = inOrder(consumer); + order.verify(consumer).onSubscribe(any(Disposable.class)); + order.verify(consumer).onError(any(IOException.class)); + order.verifyNoMoreInteractions(); + + TestHelper.assertError(errors, 0, CompositeException.class); + + CompositeException compositeException = (CompositeException)errors.get(0); + TestHelper.assertError(compositeException.getExceptions(), 0, IOException.class); + TestHelper.assertError(compositeException.getExceptions(), 1, TestException.class); + }); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleStartWithTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleStartWithTest.java new file mode 100644 index 0000000000..3d01226d9a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleStartWithTest.java @@ -0,0 +1,126 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; + +public class SingleStartWithTest { + + @Test + public void justCompletableComplete() { + Single.just(1) + .startWith(Completable.complete()) + .test() + .assertResult(1); + } + + @Test + public void justCompletableError() { + Single.just(1) + .startWith(Completable.error(new TestException())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void justSingleJust() { + Single.just(1) + .startWith(Single.just(0)) + .test() + .assertResult(0, 1); + } + + @Test + public void justSingleError() { + Single.just(1) + .startWith(Single.error(new TestException())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void justMaybeJust() { + Single.just(1) + .startWith(Maybe.just(0)) + .test() + .assertResult(0, 1); + } + + @Test + public void justMaybeEmpty() { + Single.just(1) + .startWith(Maybe.empty()) + .test() + .assertResult(1); + } + + @Test + public void justMaybeError() { + Single.just(1) + .startWith(Maybe.error(new TestException())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void justObservableJust() { + Single.just(1) + .startWith(Observable.just(-1, 0)) + .test() + .assertResult(-1, 0, 1); + } + + @Test + public void justObservableEmpty() { + Single.just(1) + .startWith(Observable.empty()) + .test() + .assertResult(1); + } + + @Test + public void justObservableError() { + Single.just(1) + .startWith(Observable.error(new TestException())) + .test() + .assertFailure(TestException.class); + } + + @Test + public void justFlowableJust() { + Single.just(1) + .startWith(Flowable.just(-1, 0)) + .test() + .assertResult(-1, 0, 1); + } + + @Test + public void justFlowableEmpty() { + Single.just(1) + .startWith(Observable.empty()) + .test() + .assertResult(1); + } + + @Test + public void justFlowableError() { + Single.just(1) + .startWith(Flowable.error(new TestException())) + .test() + .assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOnTest.java index 0c22190691..bec9782b0e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSwitchOnNextTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSwitchOnNextTest.java new file mode 100644 index 0000000000..f3c119222d --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleSwitchOnNextTest.java @@ -0,0 +1,123 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.subscribers.TestSubscriber; + +public class SingleSwitchOnNextTest extends RxJavaTest { + + @Test + public void normal() { + Single.switchOnNext( + Flowable.range(1, 5) + .map(v -> { + if (v % 2 == 0) { + return Single.just(v); + } + return Single.just(10 + v); + }) + ) + .test() + .assertResult(11, 2, 13, 4, 15); + } + + @Test + public void normalDelayError() { + Single.switchOnNextDelayError( + Flowable.range(1, 5) + .map(v -> { + if (v % 2 == 0) { + return Single.just(v); + } + return Single.just(10 + v); + }) + ) + .test() + .assertResult(11, 2, 13, 4, 15); + } + + @Test + public void noDelaySwitch() { + PublishProcessor> pp = PublishProcessor.create(); + + TestSubscriber ts = Single.switchOnNext(pp).test(); + + assertTrue(pp.hasSubscribers()); + + ts.assertEmpty(); + + SingleSubject ss1 = SingleSubject.create(); + SingleSubject ss2 = SingleSubject.create(); + + pp.onNext(ss1); + + assertTrue(ss1.hasObservers()); + + pp.onNext(ss2); + + assertFalse(ss1.hasObservers()); + assertTrue(ss2.hasObservers()); + + pp.onComplete(); + + assertTrue(ss2.hasObservers()); + + ss2.onSuccess(1); + + ts.assertResult(1); + } + + @Test + public void delaySwitch() { + PublishProcessor> pp = PublishProcessor.create(); + + TestSubscriber ts = Single.switchOnNextDelayError(pp).test(); + + assertTrue(pp.hasSubscribers()); + + ts.assertEmpty(); + + SingleSubject ss1 = SingleSubject.create(); + SingleSubject ss2 = SingleSubject.create(); + + pp.onNext(ss1); + + assertTrue(ss1.hasObservers()); + + pp.onNext(ss2); + + assertFalse(ss1.hasObservers()); + assertTrue(ss2.hasObservers()); + + assertTrue(ss2.hasObservers()); + + ss2.onError(new TestException()); + + assertTrue(pp.hasSubscribers()); + + ts.assertEmpty(); + + pp.onComplete(); + + ts.assertFailure(TestException.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntilTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntilTest.java index 0a2d27340a..c5389b0bbd 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTakeUntilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeIntervalTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeIntervalTest.java new file mode 100644 index 0000000000..ade198db50 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeIntervalTest.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.*; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleTimeIntervalTest { + + @Test + public void just() { + Single.just(1) + .timestamp() + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void error() { + Single.error(new TestException()) + .timestamp() + .test() + .assertFailure(TestException.class); + } + + @Test + public void justSeconds() { + Single.just(1) + .timestamp(TimeUnit.SECONDS) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justScheduler() { + Single.just(1) + .timestamp(Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justSecondsScheduler() { + Single.just(1) + .timestamp(TimeUnit.SECONDS, Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingle(m -> m.timestamp()); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(SingleSubject.create().timestamp()); + } + + @Test + public void timeInfo() { + TestScheduler scheduler = new TestScheduler(); + + SingleSubject ss = SingleSubject.create(); + + TestObserver> to = ss + .timestamp(scheduler) + .test(); + + scheduler.advanceTimeBy(1000, TimeUnit.MILLISECONDS); + + ss.onSuccess(1); + + to.assertResult(new Timed<>(1, 1000L, TimeUnit.MILLISECONDS)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java index 7c51e8be91..e54fc11207 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimeoutTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,12 +21,14 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Action; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.TestScheduler; +import io.reactivex.rxjava3.schedulers.*; import io.reactivex.rxjava3.subjects.*; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -216,9 +218,61 @@ public void run() { public void mainTimedOut() { Single .never() - .timeout(1, TimeUnit.NANOSECONDS) + .timeout(1, TimeUnit.MILLISECONDS) .to(TestHelper.testConsumer()) .awaitDone(5, TimeUnit.SECONDS) - .assertFailureAndMessage(TimeoutException.class, timeoutMessage(1, TimeUnit.NANOSECONDS)); + .assertFailureAndMessage(TimeoutException.class, timeoutMessage(1, TimeUnit.MILLISECONDS)); + } + + @Test + public void mainTimeoutFallbackSuccess() { + Single.never() + .timeout(1, TimeUnit.MILLISECONDS, Single.just(1)) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(1); + } + + @Test + public void timeoutBeforeOnSubscribeFromMain() { + Disposable d = Disposable.empty(); + + new Single() { + @Override + protected void subscribeActual(@NonNull SingleObserver observer) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + observer.onSubscribe(d); + } + } + .timeout(1, TimeUnit.MILLISECONDS, Single.just(1)) + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertResult(1); + + assertTrue(d.isDisposed()); + } + + @Test + public void timeoutWithZero() throws InterruptedException { + int n = 10_000; + Scheduler sch = Schedulers.single(); + for (int i = 0; i < n; i++) { + final int y = i; + final CountDownLatch latch = new CountDownLatch(1); + Disposable d = Single.never() + .timeout(0, TimeUnit.NANOSECONDS, sch) + .subscribe(v -> {}, e -> { + //System.out.println("timeout " + y); + latch.countDown(); + }); + if (!latch.await(2, TimeUnit.SECONDS)) { + System.out.println(d + " " + sch); + throw new IllegalStateException("Timeout did not work at y = " + y); + } + } } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimerTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimerTest.java index 2924364459..3b5797f498 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public void disposed() { public void timerInterruptible() throws Exception { ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); try { - for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec) }) { + for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec, true) }) { final AtomicBoolean interrupted = new AtomicBoolean(); TestObserver to = Single.timer(1, TimeUnit.MILLISECONDS, s) .map(new Function() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimestampTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimestampTest.java new file mode 100644 index 0000000000..206da8f428 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleTimestampTest.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.operators.single; + +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.schedulers.*; +import io.reactivex.rxjava3.subjects.SingleSubject; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class SingleTimestampTest { + + @Test + public void just() { + Single.just(1) + .timeInterval() + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void error() { + Single.error(new TestException()) + .timeInterval() + .test() + .assertFailure(TestException.class); + } + + @Test + public void justSeconds() { + Single.just(1) + .timeInterval(TimeUnit.SECONDS) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justScheduler() { + Single.just(1) + .timeInterval(Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void justSecondsScheduler() { + Single.just(1) + .timeInterval(TimeUnit.SECONDS, Schedulers.single()) + .test() + .assertValueCount(1) + .assertNoErrors() + .assertComplete(); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeSingle(m -> m.timeInterval()); + } + + @Test + public void dispose() { + TestHelper.checkDisposed(SingleSubject.create().timeInterval()); + } + + @Test + public void timeInfo() { + TestScheduler scheduler = new TestScheduler(); + + SingleSubject ss = SingleSubject.create(); + + TestObserver> to = ss + .timeInterval(scheduler) + .test(); + + scheduler.advanceTimeBy(1000, TimeUnit.MILLISECONDS); + + ss.onSuccess(1); + + to.assertResult(new Timed<>(1, 1000L, TimeUnit.MILLISECONDS)); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowableTest.java index f4c344c801..1c9cf06385 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservableTest.java index 35d38d49c7..f72bd4ac60 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleToObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOnTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOnTest.java index d28c47ad08..fa07de729c 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUnsubscribeOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java index c1463923cf..28c6b34ef7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleUsingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -101,7 +101,7 @@ public void errorNonEager() { @Test public void eagerMapperThrowsDisposerThrows() { - TestObserverEx to = Single.using(Functions.justSupplier(Disposables.empty()), mapperThrows, disposerThrows) + TestObserverEx to = Single.using(Functions.justSupplier(Disposable.empty()), mapperThrows, disposerThrows) .to(TestHelper.testConsumer()) .assertFailure(CompositeException.class); @@ -116,7 +116,7 @@ public void noneagerMapperThrowsDisposerThrows() { List errors = TestHelper.trackPluginErrors(); try { - Single.using(Functions.justSupplier(Disposables.empty()), mapperThrows, disposerThrows, false) + Single.using(Functions.justSupplier(Disposable.empty()), mapperThrows, disposerThrows, false) .to(TestHelper.testConsumer()) .assertFailureAndMessage(TestException.class, "Mapper"); @@ -128,7 +128,7 @@ public void noneagerMapperThrowsDisposerThrows() { @Test public void resourceDisposedIfMapperCrashes() { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); Single.using(Functions.justSupplier(d), mapperThrows, disposer) .test() @@ -139,7 +139,7 @@ public void resourceDisposedIfMapperCrashes() { @Test public void resourceDisposedIfMapperCrashesNonEager() { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); Single.using(Functions.justSupplier(d), mapperThrows, disposer, false) .test() @@ -150,7 +150,7 @@ public void resourceDisposedIfMapperCrashesNonEager() { @Test public void dispose() { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); Single.using(Functions.justSupplier(d), mapper, disposer, false) .test(true); @@ -160,7 +160,7 @@ public void dispose() { @Test public void disposerThrowsEager() { - Single.using(Functions.justSupplier(Disposables.empty()), mapper, disposerThrows) + Single.using(Functions.justSupplier(Disposable.empty()), mapper, disposerThrows) .test() .assertFailure(TestException.class); } @@ -171,7 +171,7 @@ public void disposerThrowsNonEager() { List errors = TestHelper.trackPluginErrors(); try { - Single.using(Functions.justSupplier(Disposables.empty()), mapper, disposerThrows, false) + Single.using(Functions.justSupplier(Disposable.empty()), mapper, disposerThrows, false) .test() .assertResult(1); TestHelper.assertUndeliverable(errors, 0, TestException.class, "Disposer"); @@ -182,7 +182,7 @@ public void disposerThrowsNonEager() { @Test public void errorAndDisposerThrowsEager() { - TestObserverEx to = Single.using(Functions.justSupplier(Disposables.empty()), + TestObserverEx to = Single.using(Functions.justSupplier(Disposable.empty()), new Function>() { @Override public SingleSource apply(Disposable v) throws Exception { @@ -202,7 +202,7 @@ public void errorAndDisposerThrowsNonEager() { List errors = TestHelper.trackPluginErrors(); try { - Single.using(Functions.justSupplier(Disposables.empty()), + Single.using(Functions.justSupplier(Disposable.empty()), new Function>() { @Override public SingleSource apply(Disposable v) throws Exception { @@ -222,7 +222,7 @@ public void successDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishProcessor pp = PublishProcessor.create(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); final TestObserver to = Single.using(Functions.justSupplier(d), new Function>() { @Override @@ -264,11 +264,11 @@ public SingleSource apply(Integer v) throws Exception { return new Single() { @Override protected void subscribeActual(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); assertFalse(((Disposable)observer).isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); observer.onSubscribe(d); assertTrue(d.isDisposed()); @@ -293,11 +293,12 @@ protected void subscribeActual(SingleObserver observer) { } @Test + @SuppressUndeliverable public void errorDisposeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishProcessor pp = PublishProcessor.create(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); final TestObserver to = Single.using(Functions.justSupplier(d), new Function>() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java index 18f799b3a2..df41235e38 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipArrayTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,16 +16,19 @@ import static org.junit.Assert.*; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.subjects.SingleSubject; import io.reactivex.rxjava3.testsupport.TestHelper; public class SingleZipArrayTest extends RxJavaTest { @@ -151,7 +154,6 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipArrayOneIsNull() { Single.zipArray(new Function() { @@ -176,7 +178,6 @@ public Object[] apply(Object[] a) throws Exception { .assertFailure(NoSuchElementException.class); } - @SuppressWarnings("unchecked") @Test public void oneArray() { Single.zipArray(new Function() { @@ -189,11 +190,46 @@ public Object apply(Object[] a) throws Exception { .assertResult(2); } - @SuppressWarnings("unchecked") @Test public void singleSourceZipperReturnsNull() { Single.zipArray(Functions.justFunction(null), Single.just(1)) .to(TestHelper.testConsumer()) .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); } + + @Test + public void singleSourceZipperReturnsNull2() { + Single.zipArray(Functions.justFunction(null), Single.just(1), Single.just(2)) + .to(TestHelper.testConsumer()) + .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); + } + + @Test + public void dispose2() { + TestHelper.checkDisposed(Single.zipArray(Functions.justFunction(1), SingleSubject.create(), SingleSubject.create())); + } + + @Test + public void bothSucceed() { + Single.zipArray(a -> Arrays.asList(a), Single.just(1), Single.just(2)) + .test() + .assertResult(Arrays.asList(1, 2)); + } + + @Test + public void onSuccessAfterDispose() { + AtomicReference> emitter = new AtomicReference<>(); + + TestObserver> to = Single.zipArray(Arrays::asList, + (SingleSource)o -> emitter.set(o), Single.never()) + .test(); + + emitter.get().onSubscribe(Disposable.empty()); + + to.dispose(); + + emitter.get().onSuccess(1); + + to.assertEmpty(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterableTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterableTest.java index 1a94e23257..c2293a2b89 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipIterableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -38,7 +38,6 @@ public Object apply(Object[] a) throws Exception { } }; - @SuppressWarnings("unchecked") @Test public void firstError() { Single.zip(Arrays.asList(Single.error(new TestException()), Single.just(1)), addString) @@ -46,7 +45,6 @@ public void firstError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void secondError() { Single.zip(Arrays.asList(Single.just(1), Single.error(new TestException())), addString) @@ -54,7 +52,6 @@ public void secondError() { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void dispose() { PublishProcessor pp = PublishProcessor.create(); @@ -69,7 +66,6 @@ public void dispose() { assertFalse(pp.hasSubscribers()); } - @SuppressWarnings("unchecked") @Test public void zipperThrows() { Single.zip(Arrays.asList(Single.just(1), Single.just(2)), new Function() { @@ -82,7 +78,6 @@ public Object apply(Object[] b) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void zipperReturnsNull() { Single.zip(Arrays.asList(Single.just(1), Single.just(2)), new Function() { @@ -95,7 +90,6 @@ public Object apply(Object[] a) throws Exception { .assertFailure(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void middleError() { PublishProcessor pp0 = PublishProcessor.create(); @@ -112,7 +106,6 @@ public void middleError() { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void innerErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -156,7 +149,7 @@ public void run() { @Test public void iteratorThrows() { - Single.zip(new CrashingMappedIterable>(1, 100, 100, new Function>() { + Single.zip(new CrashingMappedIterable<>(1, 100, 100, new Function>() { @Override public Single apply(Integer v) throws Exception { return Single.just(v); @@ -168,7 +161,7 @@ public Single apply(Integer v) throws Exception { @Test public void hasNextThrows() { - Single.zip(new CrashingMappedIterable>(100, 20, 100, new Function>() { + Single.zip(new CrashingMappedIterable<>(100, 20, 100, new Function>() { @Override public Single apply(Integer v) throws Exception { return Single.just(v); @@ -180,7 +173,7 @@ public Single apply(Integer v) throws Exception { @Test public void nextThrows() { - Single.zip(new CrashingMappedIterable>(100, 100, 5, new Function>() { + Single.zip(new CrashingMappedIterable<>(100, 100, 5, new Function>() { @Override public Single apply(Integer v) throws Exception { return Single.just(v); @@ -190,7 +183,6 @@ public Single apply(Integer v) throws Exception { .assertFailureAndMessage(TestException.class, "next()"); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableOneIsNull() { Single.zip(Arrays.asList(null, Single.just(1)), new Function() { @@ -202,7 +194,6 @@ public Object apply(Object[] v) { .blockingGet(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableTwoIsNull() { Single.zip(Arrays.asList(Single.just(1), null), new Function() { @@ -238,7 +229,6 @@ public Object apply(Object[] a) throws Exception { .assertResult(2); } - @SuppressWarnings("unchecked") @Test public void singleSourceZipperReturnsNull() { Single.zip(Arrays.asList(Single.just(1)), Functions.justFunction(null)) @@ -246,7 +236,6 @@ public void singleSourceZipperReturnsNull() { .assertFailureAndMessage(NullPointerException.class, "The zipper returned a null value"); } - @SuppressWarnings("unchecked") @Test public void singleSourcesInIterable() { SingleSource source = new SingleSource() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipTest.java b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipTest.java index f5ae16d0bc..14f2e68267 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/operators/single/SingleZipTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -163,7 +163,6 @@ public Integer apply(Integer a, Integer b) throws Exception { assertEquals(0, counter.get()); } - @SuppressWarnings("unchecked") @Test public void noDisposeOnAllSuccess2() { final AtomicInteger counter = new AtomicInteger(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java index 85507fb7f1..cd10a10443 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/AbstractDirectTaskTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class AbstractDirectTaskTest extends RxJavaTest { @Test public void cancelSetFuture() { - AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; final Boolean[] interrupted = { null }; @@ -58,7 +58,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Test public void cancelSetFutureCurrentThread() { - AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; final Boolean[] interrupted = { null }; @@ -91,7 +91,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Test public void setFutureCancel() { - AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; final Boolean[] interrupted = { null }; @@ -119,7 +119,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Test public void setFutureCancelSameThread() { - AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; final Boolean[] interrupted = { null }; @@ -148,7 +148,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Test public void finished() { - AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; final Boolean[] interrupted = { null }; @@ -177,7 +177,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Test public void finishedCancel() { - AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; final Boolean[] interrupted = { null }; @@ -211,7 +211,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Test public void disposeSetFutureRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE) { + final AbstractDirectTask task = new AbstractDirectTask(Functions.EMPTY_RUNNABLE, true) { private static final long serialVersionUID = 208585707945686116L; }; @@ -241,4 +241,31 @@ public void run() { TestHelper.race(r1, r2); } } + + static class TestDirectTask extends AbstractDirectTask { + private static final long serialVersionUID = 587679821055711738L; + + TestDirectTask() { + super(Functions.EMPTY_RUNNABLE, true); + } + } + + @Test + public void toStringStates() { + TestDirectTask task = new TestDirectTask(); + + assertEquals("TestDirectTask[Waiting]", task.toString()); + + task.runner = Thread.currentThread(); + + assertEquals("TestDirectTask[Running on " + Thread.currentThread() + "]", task.toString()); + + task.dispose(); + + assertEquals("TestDirectTask[Disposed]", task.toString()); + + task.set(AbstractDirectTask.FINISHED); + + assertEquals("TestDirectTask[Finished]", task.toString()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java new file mode 100644 index 0000000000..237bb233eb --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/BooleanRunnableTest.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import static org.junit.Assert.fail; + +import java.util.List; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler.ExecutorWorker.BooleanRunnable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class BooleanRunnableTest extends RxJavaTest { + + @Test + public void runnableThrows() { + List errors = TestHelper.trackPluginErrors(); + try { + BooleanRunnable task = new BooleanRunnable(() -> { + throw new TestException(); + }); + + try { + task.run(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ComputationSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ComputationSchedulerInternalTest.java index 59ad5d1dfc..5c8aa1a65f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ComputationSchedulerInternalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ComputationSchedulerInternalTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.schedulers; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancelTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancelTest.java index 19a68d0818..224e33ae7d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancelTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/DisposeOnCancelTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public class DisposeOnCancelTest extends RxJavaTest { @Test public void basicCoverage() throws Exception { - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); DisposeOnCancel doc = new DisposeOnCancel(d); diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java index 08e06b189e..31c3112255 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerDelayedRunnableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,10 +22,12 @@ import io.reactivex.rxjava3.core.RxJavaTest; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler.DelayedRunnable; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class ExecutorSchedulerDelayedRunnableTest extends RxJavaTest { @Test(expected = TestException.class) + @SuppressUndeliverable public void delayedRunnableCrash() { DelayedRunnable dl = new DelayedRunnable(new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerInternalTest.java new file mode 100644 index 0000000000..64d4d1861c --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ExecutorSchedulerInternalTest.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +public class ExecutorSchedulerInternalTest { + + @Test + public void helperHolder() { + assertNotNull(new ExecutorScheduler.SingleHolder()); + } + +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinSchedulerTest.java index cacb8fabeb..75710644d9 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ImmediateThinSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java index 577aa468de..4a57e86781 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InstantPeriodicTaskTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -44,7 +41,12 @@ public void run() { } }, exec); - assertNull(task.call()); + try { + task.call(); + fail("Should have thrown!"); + } catch (TestException excepted) { + // excepted + } TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { @@ -156,12 +158,12 @@ public void run() { task.dispose(); - FutureTask f1 = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f1 = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); task.setFirst(f1); assertTrue(f1.isCancelled()); - FutureTask f2 = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f2 = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); task.setRest(f2); assertTrue(f2.isCancelled()); @@ -187,12 +189,12 @@ public void run() { task.dispose(); - FutureTask f1 = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f1 = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); task.setFirst(f1); assertTrue(f1.isCancelled()); - FutureTask f2 = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f2 = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); task.setRest(f2); assertTrue(f2.isCancelled()); @@ -214,7 +216,7 @@ public void run() { } }, exec); - final FutureTask f1 = new FutureTask(Functions.EMPTY_RUNNABLE, null); + final FutureTask f1 = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); Runnable r1 = new Runnable() { @Override public void run() { @@ -251,7 +253,7 @@ public void run() { } }, exec); - final FutureTask f1 = new FutureTask(Functions.EMPTY_RUNNABLE, null); + final FutureTask f1 = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); Runnable r1 = new Runnable() { @Override public void run() { diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java new file mode 100644 index 0000000000..e040b37419 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/InterruptibleRunnableTest.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import static org.junit.Assert.fail; + +import java.util.List; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler.ExecutorWorker.InterruptibleRunnable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class InterruptibleRunnableTest extends RxJavaTest { + + @Test + public void runnableThrows() { + List errors = TestHelper.trackPluginErrors(); + try { + InterruptibleRunnable task = new InterruptibleRunnable(() -> { + throw new TestException(); + }, null); + + try { + task.run(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java new file mode 100644 index 0000000000..e3f55b75fb --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoScheduledReleaseTest.java @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.schedulers.Schedulers; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +public class IoScheduledReleaseTest extends RxJavaTest { + + /* This test will be stuck in a deadlock if IoScheduler.USE_SCHEDULED_RELEASE is not set */ + @Test + public void scheduledRelease() { + boolean savedScheduledRelease = IoScheduler.USE_SCHEDULED_RELEASE; + IoScheduler.USE_SCHEDULED_RELEASE = true; + try { + Flowable.just("item") + .observeOn(Schedulers.io()) + .firstOrError() + .map(item -> { + for (int i = 0; i < 50; i++) { + Completable.complete() + .observeOn(Schedulers.io()) + .blockingAwait(); + } + return "Done"; + }) + .ignoreElement() + .test() + .awaitDone(5, TimeUnit.SECONDS) + .assertComplete(); + } finally { + IoScheduler.USE_SCHEDULED_RELEASE = savedScheduledRelease; + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java new file mode 100644 index 0000000000..9a4b6f3be0 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/IoSchedulerInternalTest.java @@ -0,0 +1,95 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.schedulers; + +import static org.junit.Assert.*; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.internal.schedulers.IoScheduler.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class IoSchedulerInternalTest extends RxJavaTest { + + @Test + public void expiredQueueEmpty() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + CachedWorkerPool.evictExpiredWorkers(expire, cd); + } + + @Test + public void expiredWorkerRemoved() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + ThreadWorker tw = new ThreadWorker(new RxThreadFactory("IoExpiryTest")); + + try { + expire.add(tw); + cd.add(tw); + + CachedWorkerPool.evictExpiredWorkers(expire, cd); + + assertTrue(tw.isDisposed()); + assertTrue(expire.isEmpty()); + } finally { + tw.dispose(); + } + } + + @Test + public void noExpiredWorker() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + ThreadWorker tw = new ThreadWorker(new RxThreadFactory("IoExpiryTest")); + tw.setExpirationTime(System.nanoTime() + 10_000_000_000L); + + try { + expire.add(tw); + cd.add(tw); + + CachedWorkerPool.evictExpiredWorkers(expire, cd); + + assertFalse(tw.isDisposed()); + assertFalse(expire.isEmpty()); + } finally { + tw.dispose(); + } + } + + @Test + public void expireReuseRace() { + ConcurrentLinkedQueue expire = new ConcurrentLinkedQueue<>(); + CompositeDisposable cd = new CompositeDisposable(); + + ThreadWorker tw = new ThreadWorker(new RxThreadFactory("IoExpiryTest")); + tw.dispose(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + expire.add(tw); + cd.add(tw); + + TestHelper.race( + () -> CachedWorkerPool.evictExpiredWorkers(expire, cd), + () -> expire.remove(tw) + ); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactoryTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactoryTest.java index 83a65e65ac..d0cbb6a476 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactoryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/RxThreadFactoryTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java index 23afeca2ce..d41906b13a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledDirectPeriodicTaskTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,6 +13,8 @@ package io.reactivex.rxjava3.internal.schedulers; +import static org.junit.Assert.fail; + import java.util.List; import org.junit.Test; @@ -33,9 +35,14 @@ public void runnableThrows() { public void run() { throw new TestException(); } - }); - - task.run(); + }, true); + + try { + task.run(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java index 58305b7e9e..8d2fc1f7cc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/ScheduledRunnableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -65,7 +65,7 @@ public void setFutureCancelRace() { final ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); - final FutureTask ft = new FutureTask(Functions.EMPTY_RUNNABLE, 0); + final FutureTask ft = new FutureTask<>(Functions.EMPTY_RUNNABLE, 0); Runnable r1 = new Runnable() { @Override @@ -94,7 +94,7 @@ public void setFutureRunRace() { final ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); - final FutureTask ft = new FutureTask(Functions.EMPTY_RUNNABLE, 0); + final FutureTask ft = new FutureTask<>(Functions.EMPTY_RUNNABLE, 0); Runnable r1 = new Runnable() { @Override @@ -208,7 +208,12 @@ public void run() { }, set); set.add(run); - run.run(); + try { + run.run(); + fail("Should have thrown!"); + } catch (TestException expected) { + // expected + } assertTrue(run.isDisposed()); @@ -266,7 +271,7 @@ public void runFuture() { final ScheduledRunnable run = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); set.add(run); - final FutureTask ft = new FutureTask(Functions.EMPTY_RUNNABLE, null); + final FutureTask ft = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); Runnable r1 = new Runnable() { @Override @@ -316,7 +321,7 @@ public void run() { final ScheduledRunnable run = new ScheduledRunnable(r0, set); set.add(run); - final FutureTask ft = new FutureTask(run, null); + final FutureTask ft = new FutureTask<>(run, null); Runnable r2 = new Runnable() { @Override @@ -394,4 +399,29 @@ public void withParentIsDisposed() { assertFalse(set.remove(run)); } + + @Test + public void toStringStates() { + CompositeDisposable set = new CompositeDisposable(); + ScheduledRunnable task = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); + + assertEquals("ScheduledRunnable[Waiting]", task.toString()); + + task.set(ScheduledRunnable.THREAD_INDEX, Thread.currentThread()); + + assertEquals("ScheduledRunnable[Running on " + Thread.currentThread() + "]", task.toString()); + + task.dispose(); + + assertEquals("ScheduledRunnable[Disposed(Sync)]", task.toString()); + + task.set(ScheduledRunnable.FUTURE_INDEX, ScheduledRunnable.DONE); + + assertEquals("ScheduledRunnable[Finished]", task.toString()); + + task = new ScheduledRunnable(Functions.EMPTY_RUNNABLE, set); + task.dispose(); + + assertEquals("ScheduledRunnable[Disposed(Async)]", task.toString()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupportTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupportTest.java index 332a115604..3d16867a1a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupportTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerMultiWorkerSupportTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public class SchedulerMultiWorkerSupportTest extends RxJavaTest { @Test public void moreThanMaxWorkers() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); SchedulerMultiWorkerSupport mws = (SchedulerMultiWorkerSupport)Schedulers.computation(); @@ -49,7 +49,7 @@ public void onWorker(int i, Worker w) { @Test public void getShutdownWorkers() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); ComputationScheduler.NONE.createWorkers(max * 2, new WorkerCallback() { @Override @@ -74,14 +74,14 @@ public void distinctThreads() throws Exception { try { final CountDownLatch cdl = new CountDownLatch(max * 2); - final Set threads1 = Collections.synchronizedSet(new HashSet()); + final Set threads1 = Collections.synchronizedSet(new HashSet<>()); - final Set threads2 = Collections.synchronizedSet(new HashSet()); + final Set threads2 = Collections.synchronizedSet(new HashSet<>()); Runnable parallel1 = new Runnable() { @Override public void run() { - final List list1 = new ArrayList(); + final List list1 = new ArrayList<>(); SchedulerMultiWorkerSupport mws = (SchedulerMultiWorkerSupport)Schedulers.computation(); @@ -110,7 +110,7 @@ public void run() { Runnable parallel2 = new Runnable() { @Override public void run() { - final List list2 = new ArrayList(); + final List list2 = new ArrayList<>(); SchedulerMultiWorkerSupport mws = (SchedulerMultiWorkerSupport)Schedulers.computation(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java index 1fcbf105b4..12afa708c4 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerPoolFactoryTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -23,7 +20,6 @@ import io.reactivex.rxjava3.core.RxJavaTest; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; public class SchedulerPoolFactoryTest extends RxJavaTest { @@ -33,50 +29,6 @@ public void utilityClass() { TestHelper.checkUtilityClass(SchedulerPoolFactory.class); } - @Test - public void multiStartStop() { - SchedulerPoolFactory.shutdown(); - - SchedulerPoolFactory.shutdown(); - - SchedulerPoolFactory.tryStart(false); - - assertNull(SchedulerPoolFactory.PURGE_THREAD.get()); - - SchedulerPoolFactory.start(); - - // restart schedulers - Schedulers.shutdown(); - - Schedulers.start(); - } - - @Test - public void startRace() throws InterruptedException { - try { - for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - SchedulerPoolFactory.shutdown(); - - Runnable r1 = new Runnable() { - @Override - public void run() { - SchedulerPoolFactory.start(); - } - }; - - TestHelper.race(r1, r1); - } - - } finally { - // restart schedulers - Schedulers.shutdown(); - - Thread.sleep(200); - - Schedulers.start(); - } - } - @Test public void boolPropertiesDisabledReturnsDefaultDisabled() throws Throwable { assertTrue(SchedulerPoolFactory.getBooleanProperty(false, "key", false, true, failingPropertiesAccessor)); @@ -101,30 +53,6 @@ public void boolPropertiesReturnsValue() throws Throwable { assertFalse(SchedulerPoolFactory.getBooleanProperty(true, "false", false, true, Functions.identity())); } - @Test - public void intPropertiesDisabledReturnsDefaultDisabled() throws Throwable { - assertEquals(-1, SchedulerPoolFactory.getIntProperty(false, "key", 0, -1, failingPropertiesAccessor)); - assertEquals(-1, SchedulerPoolFactory.getIntProperty(false, "key", 1, -1, failingPropertiesAccessor)); - } - - @Test - public void intPropertiesEnabledMissingReturnsDefaultMissing() throws Throwable { - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 0, missingPropertiesAccessor)); - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 1, missingPropertiesAccessor)); - } - - @Test - public void intPropertiesFailureReturnsDefaultMissing() throws Throwable { - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 0, failingPropertiesAccessor)); - assertEquals(-1, SchedulerPoolFactory.getIntProperty(true, "key", -1, 1, failingPropertiesAccessor)); - } - - @Test - public void intPropertiesReturnsValue() throws Throwable { - assertEquals(1, SchedulerPoolFactory.getIntProperty(true, "1", 0, 4, Functions.identity())); - assertEquals(2, SchedulerPoolFactory.getIntProperty(true, "2", 3, 5, Functions.identity())); - } - static final Function failingPropertiesAccessor = new Function() { @Override public String apply(String v) throws Throwable { @@ -138,22 +66,4 @@ public String apply(String v) throws Throwable { return null; } }; - - @Test - public void putIntoPoolNoPurge() { - int s = SchedulerPoolFactory.POOLS.size(); - - SchedulerPoolFactory.tryPutIntoPool(false, null); - - assertEquals(s, SchedulerPoolFactory.POOLS.size()); - } - - @Test - public void putIntoPoolNonThreadPool() { - int s = SchedulerPoolFactory.POOLS.size(); - - SchedulerPoolFactory.tryPutIntoPool(true, null); - - assertEquals(s, SchedulerPoolFactory.POOLS.size()); - } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhenTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhenTest.java index 8225fd4e06..03e6a9300f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhenTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SchedulerWhenTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -268,7 +268,7 @@ public void scheduledActiondisposedSetRace() { @Override protected Disposable callActual(Worker actualWorker, CompletableObserver actionCompletable) { - return Disposables.empty(); + return Disposable.empty(); } }; @@ -299,7 +299,7 @@ public void scheduledActionStates() { protected Disposable callActual(Worker actualWorker, CompletableObserver actionCompletable) { count.incrementAndGet(); - return Disposables.empty(); + return Disposable.empty(); } }; @@ -320,7 +320,7 @@ protected Disposable callActual(Worker actualWorker, assertEquals(0, count.get()); // should not run when already scheduled - sa.set(Disposables.empty()); + sa.set(Disposable.empty()); sa.call(Schedulers.single().createWorker(), null); @@ -336,7 +336,7 @@ protected Disposable callActual(Worker actualWorker, CompletableObserver actionCompletable) { count.incrementAndGet(); dispose(); - return Disposables.empty(); + return Disposable.empty(); } }; diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java index 82f66ceff6..c9f9cc5476 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/SingleSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,22 +14,25 @@ package io.reactivex.rxjava3.internal.schedulers; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import org.junit.Test; import io.reactivex.rxjava3.core.Scheduler; import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.SingleScheduler.ScheduledWorker; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class SingleSchedulerTest extends AbstractSchedulerTests { @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; @@ -43,20 +46,20 @@ public void run() { Scheduler s = new SingleScheduler(); s.shutdown(); - assertEquals(Disposables.disposed(), s.scheduleDirect(r)); + assertEquals(Disposable.disposed(), s.scheduleDirect(r)); - assertEquals(Disposables.disposed(), s.scheduleDirect(r, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), s.scheduleDirect(r, 1, TimeUnit.SECONDS)); - assertEquals(Disposables.disposed(), s.schedulePeriodicallyDirect(r, 1, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), s.schedulePeriodicallyDirect(r, 1, 1, TimeUnit.SECONDS)); Worker w = s.createWorker(); ((ScheduledWorker)w).executor.shutdownNow(); - assertEquals(Disposables.disposed(), w.schedule(r)); + assertEquals(Disposable.disposed(), w.schedule(r)); - assertEquals(Disposables.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); - assertEquals(Disposables.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); assertEquals(0, calls[0]); @@ -123,4 +126,20 @@ public void runnableDisposedAsyncTimed() throws Exception { return Schedulers.single(); } + @Test + public void zeroPeriodRejectedExecution() throws Throwable { + TestHelper.withErrorTracking(errors -> { + Scheduler s = RxJavaPlugins.createSingleScheduler(new RxThreadFactory("Test")); + s.shutdown(); + Runnable run = mock(Runnable.class); + + s.schedulePeriodicallyDirect(run, 1, 0, TimeUnit.MILLISECONDS); + + Thread.sleep(100); + + verify(run, never()).run(); + + TestHelper.assertUndeliverable(errors, 0, RejectedExecutionException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java b/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java index e30400214c..b25620e2bf 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/schedulers/TrampolineSchedulerInternalTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.schedulers; @@ -20,19 +17,23 @@ import static org.mockito.Mockito.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; -import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.TrampolineScheduler.*; import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.testsupport.*; public class TrampolineSchedulerInternalTest extends RxJavaTest { @Test + @SuppressUndeliverable public void scheduleDirectInterrupt() { Thread.currentThread().interrupt(); @@ -144,6 +145,7 @@ public void run() { } @Test + @SuppressUndeliverable public void reentrantScheduleInterrupt() { final Worker w = Schedulers.trampoline().createWorker(); try { @@ -209,4 +211,29 @@ public void run() { verify(r, never()).run(); } + + @Test + public void submitAndDisposeNextTask() { + Scheduler.Worker w = Schedulers.trampoline().createWorker(); + + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + Runnable run = mock(Runnable.class); + + AtomicInteger sync = new AtomicInteger(2); + + w.schedule(() -> { + Disposable d = w.schedule(run); + + Schedulers.single().scheduleDirect(() -> { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + d.dispose(); + }); + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + }); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java index 0bcfd2d12a..c58d015abc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,15 +14,18 @@ package io.reactivex.rxjava3.internal.subscribers; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import org.junit.Test; import org.reactivestreams.Subscription; -import io.reactivex.rxjava3.annotations.Nullable; -import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.fuseable.ConditionalSubscriber; -import io.reactivex.rxjava3.internal.subscriptions.ScalarSubscription; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.annotations.*; +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.testsupport.*; public class BasicFuseableConditionalSubscriberTest extends RxJavaTest { @@ -75,7 +78,7 @@ public Integer poll() throws Exception { } }; - fcs.onSubscribe(new ScalarSubscription(fcs, 1)); + fcs.onSubscribe(new ScalarSubscription<>(fcs, 1)); TestHelper.assertNoOffer(fcs); @@ -83,4 +86,172 @@ public Integer poll() throws Exception { fcs.clear(); assertTrue(fcs.isEmpty()); } + + @Test + public void implementationStopsOnSubscribe() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(new BooleanSubscription()); + + verify(ts, never()).onSubscribe(any()); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f + .map(v -> v) + .filter(v -> true) + ); + } + + @Test + public void transitiveBoundaryFusionNone() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(new BooleanSubscription()); + + assertEquals(QueueFuseable.NONE, bfs.transitiveBoundaryFusion(QueueFuseable.ANY)); + } + + @Test + public void transitiveBoundaryFusionAsync() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(EmptySubscription.INSTANCE); + + assertEquals(QueueFuseable.ASYNC, bfs.transitiveBoundaryFusion(QueueFuseable.ANY)); + } + + @Test + public void transitiveBoundaryFusionAsyncBoundary() { + @SuppressWarnings("unchecked") + ConditionalSubscriber ts = mock(ConditionalSubscriber.class); + + BasicFuseableConditionalSubscriber bfs = new BasicFuseableConditionalSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean tryOnNext(@NonNull Integer t) { + // TODO Auto-generated method stub + return false; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(EmptySubscription.INSTANCE); + + assertEquals(QueueFuseable.NONE, bfs.transitiveBoundaryFusion(QueueFuseable.ANY | QueueFuseable.BOUNDARY)); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java index b3543ad7d3..d6f85acc46 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,9 +17,9 @@ import org.junit.Test; -import io.reactivex.rxjava3.annotations.Nullable; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.subscriptions.ScalarSubscription; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -27,7 +27,7 @@ public class BasicFuseableSubscriberTest extends RxJavaTest { @Test public void offerThrows() { - BasicFuseableSubscriber fcs = new BasicFuseableSubscriber(new TestSubscriber(0L)) { + BasicFuseableSubscriber fcs = new BasicFuseableSubscriber(new TestSubscriber<>(0L)) { @Override public void onNext(Integer t) { @@ -45,7 +45,7 @@ public Integer poll() throws Exception { } }; - fcs.onSubscribe(new ScalarSubscription(fcs, 1)); + fcs.onSubscribe(new ScalarSubscription<>(fcs, 1)); TestHelper.assertNoOffer(fcs); @@ -53,4 +53,36 @@ public Integer poll() throws Exception { fcs.clear(); assertTrue(fcs.isEmpty()); } + + @Test + public void implementationStopsOnSubscribe() { + TestSubscriber ts = new TestSubscriber<>(); + BasicFuseableSubscriber bfs = new BasicFuseableSubscriber(ts) { + + @Override + protected boolean beforeDownstream() { + return false; + } + + @Override + public void onNext(@NonNull Integer t) { + ts.onNext(t); + } + + @Override + public int requestFusion(int mode) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public @Nullable Integer poll() throws Throwable { + return null; + } + }; + + bfs.onSubscribe(new BooleanSubscription()); + + assertFalse(ts.hasSubscription()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriberTest.java index f127e60c44..a97f4ee93e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BlockingSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,12 +29,12 @@ public class BlockingSubscriberTest extends RxJavaTest { @Test public void doubleOnSubscribe() { - TestHelper.doubleOnSubscribe(new BlockingSubscriber(new ArrayDeque())); + TestHelper.doubleOnSubscribe(new BlockingSubscriber(new ArrayDeque<>())); } @Test public void cancel() { - BlockingSubscriber bq = new BlockingSubscriber(new ArrayDeque()); + BlockingSubscriber bq = new BlockingSubscriber<>(new ArrayDeque<>()); assertFalse(bq.isCancelled()); @@ -54,7 +54,7 @@ public void blockingFirstDoubleOnSubscribe() { @Test public void blockingFirstTimeout() { - BlockingFirstSubscriber bf = new BlockingFirstSubscriber(); + BlockingFirstSubscriber bf = new BlockingFirstSubscriber<>(); Thread.currentThread().interrupt(); @@ -68,7 +68,7 @@ public void blockingFirstTimeout() { @Test public void blockingFirstTimeout2() { - BlockingFirstSubscriber bf = new BlockingFirstSubscriber(); + BlockingFirstSubscriber bf = new BlockingFirstSubscriber<>(); bf.onSubscribe(new BooleanSubscription()); @@ -85,7 +85,7 @@ public void blockingFirstTimeout2() { @Test public void cancelOnRequest() { - final BlockingFirstSubscriber bf = new BlockingFirstSubscriber(); + final BlockingFirstSubscriber bf = new BlockingFirstSubscriber<>(); final AtomicBoolean b = new AtomicBoolean(); @@ -109,7 +109,7 @@ public void cancel() { @Test public void cancelUpfront() { - final BlockingFirstSubscriber bf = new BlockingFirstSubscriber(); + final BlockingFirstSubscriber bf = new BlockingFirstSubscriber<>(); final AtomicBoolean b = new AtomicBoolean(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java index c2d843fb23..68e955097d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/BoundedSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,15 +27,15 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class BoundedSubscriberTest extends RxJavaTest { @Test public void onSubscribeThrows() { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - BoundedSubscriber subscriber = new BoundedSubscriber(new Consumer() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Object o) throws Exception { received.add(o); @@ -69,9 +69,9 @@ public void accept(Subscription subscription) throws Exception { @Test public void onNextThrows() { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - BoundedSubscriber subscriber = new BoundedSubscriber(new Consumer() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Object o) throws Exception { throw new TestException(); @@ -108,9 +108,9 @@ public void onErrorThrows() { List errors = TestHelper.trackPluginErrors(); try { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - BoundedSubscriber subscriber = new BoundedSubscriber(new Consumer() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Object o) throws Exception { received.add(o); @@ -154,9 +154,9 @@ public void onCompleteThrows() { List errors = TestHelper.trackPluginErrors(); try { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - BoundedSubscriber subscriber = new BoundedSubscriber(new Consumer() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Object o) throws Exception { received.add(o); @@ -196,9 +196,9 @@ public void accept(Subscription subscription) throws Exception { public void onNextThrowsCancelsUpstream() { PublishProcessor pp = PublishProcessor.create(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); - BoundedSubscriber s = new BoundedSubscriber(new Consumer() { + BoundedSubscriber s = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Integer v) throws Exception { throw new TestException(); @@ -237,9 +237,9 @@ public void accept(Subscription subscription) throws Exception { public void onSubscribeThrowsCancelsUpstream() { PublishProcessor pp = PublishProcessor.create(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); - BoundedSubscriber s = new BoundedSubscriber(new Consumer() { + BoundedSubscriber s = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Integer v) throws Exception { } @@ -285,9 +285,9 @@ public void subscribe(Subscriber s) { } }); - final List received = new ArrayList(); + final List received = new ArrayList<>(); - BoundedSubscriber subscriber = new BoundedSubscriber(new Consumer() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); @@ -315,6 +315,7 @@ public void accept(Subscription s) throws Exception { } @Test + @SuppressUndeliverable public void badSourceEmitAfterDone() { Flowable source = Flowable.fromPublisher(new Publisher() { @Override @@ -330,9 +331,9 @@ public void subscribe(Subscriber s) { } }); - final List received = new ArrayList(); + final List received = new ArrayList<>(); - BoundedSubscriber subscriber = new BoundedSubscriber(new Consumer() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); @@ -361,7 +362,7 @@ public void accept(Subscription s) throws Exception { @Test public void onErrorMissingShouldReportNoCustomOnError() { - BoundedSubscriber subscriber = new BoundedSubscriber(Functions.emptyConsumer(), + BoundedSubscriber subscriber = new BoundedSubscriber<>(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION, Functions.boundedConsumer(128), 128); @@ -371,11 +372,44 @@ public void onErrorMissingShouldReportNoCustomOnError() { @Test public void customOnErrorShouldReportCustomOnError() { - BoundedSubscriber subscriber = new BoundedSubscriber(Functions.emptyConsumer(), + BoundedSubscriber subscriber = new BoundedSubscriber<>(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.boundedConsumer(128), 128); assertTrue(subscriber.hasCustomOnError()); } + + @Test + public void cancel() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(Functions.emptyConsumer(), + Functions.emptyConsumer(), + Functions.EMPTY_ACTION, + Functions.boundedConsumer(128), 128); + + BooleanSubscription bs = new BooleanSubscription(); + subscriber.onSubscribe(bs); + + subscriber.cancel(); + + assertTrue(bs.isCancelled()); + } + + @Test + public void dispose() { + BoundedSubscriber subscriber = new BoundedSubscriber<>(Functions.emptyConsumer(), + Functions.emptyConsumer(), + Functions.EMPTY_ACTION, + Functions.boundedConsumer(128), 128); + + BooleanSubscription bs = new BooleanSubscription(); + subscriber.onSubscribe(bs); + + assertFalse(subscriber.isDisposed()); + + subscriber.dispose(); + + assertTrue(bs.isCancelled()); + assertTrue(subscriber.isDisposed()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriberTest.java index fcc3a2f8ea..dcfcb82377 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/DeferredScalarSubscriberTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.internal.subscribers; @@ -427,7 +424,7 @@ public void downstreamRequest(long n) { @Test public void doubleOnSubscribe() { - TestHelper.doubleOnSubscribe(new DeferredScalarSubscriber(new TestSubscriber()) { + TestHelper.doubleOnSubscribe(new DeferredScalarSubscriber(new TestSubscriber<>()) { private static final long serialVersionUID = -4445381578878059054L; @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/EmptyComponentTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/EmptyComponentTest.java index d2d9b91163..c5cf3c88ca 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/EmptyComponentTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/EmptyComponentTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,7 +44,7 @@ public void normal() { c.request(-10); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); c.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java new file mode 100644 index 0000000000..cc7083cde7 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FlowableConsumersTest.java @@ -0,0 +1,328 @@ +/* + * 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. + */ + +/* + * Copyright 2016-2019 David Karnok + * + * 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 io.reactivex.rxjava3.internal.subscribers; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.*; + +import org.junit.Test; +import org.reactivestreams.Subscriber; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.exceptions.*; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.observers.LambdaConsumerIntrospection; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.processors.PublishProcessor; +import io.reactivex.rxjava3.testsupport.TestHelper; + +public class FlowableConsumersTest implements Consumer, Action { + + final CompositeDisposable composite = new CompositeDisposable(); + + final PublishProcessor processor = PublishProcessor.create(); + + final List events = new ArrayList<>(); + + @Override + public void run() throws Exception { + events.add("OnComplete"); + } + + @Override + public void accept(Object t) throws Exception { + events.add(t); + } + + static Disposable subscribeAutoDispose(Flowable source, CompositeDisposable composite, + Consumer onNext, Consumer onError, Action onComplete) { + return source.subscribe(onNext, onError, onComplete, composite); + } + + @Test + public void onNextNormal() { + + Disposable d = subscribeAutoDispose(processor, composite, this, Functions.ON_ERROR_MISSING, () -> { }); + + assertFalse(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorNormal() { + + subscribeAutoDispose(processor, composite, this, this, () -> { }); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onErrorError() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(d.getClass().toString(), ((LambdaConsumerIntrospection)d).hasCustomOnError()); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteNormal() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onComplete(); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteError() { + + subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + processor.onNext(1); + + assertTrue(composite.size() > 0); + + assertEquals(Arrays.asList(1), events); + + processor.onError(new IOException()); + + assertEquals(events.toString(), 1, events.get(0)); + assertTrue(events.toString(), events.get(1) instanceof IOException); + + assertEquals(0, composite.size()); + } + + @Test + public void onCompleteDispose() { + + Disposable d = subscribeAutoDispose(processor, composite, this, this, this); + + assertTrue(composite.size() > 0); + + assertTrue(events.toString(), events.isEmpty()); + + assertFalse(d.isDisposed()); + + d.dispose(); + d.dispose(); + + assertTrue(d.isDisposed()); + + assertEquals(0, composite.size()); + + assertFalse(processor.hasSubscribers()); + } + + @Test + public void onNextCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, this, this); + + processor.onNext(1); + + assertTrue(errors.toString(), errors.isEmpty()); + + assertTrue(events.toString(), events.get(0) instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashOnError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, new Consumer() { + @Override + public void accept(Throwable t) throws Exception { + throw new IOException(t); + } + }, this); + + processor.onError(new IllegalArgumentException()); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, CompositeException.class); + List inners = TestHelper.compositeList(errors.get(0)); + TestHelper.assertError(inners, 0, IllegalArgumentException.class); + TestHelper.assertError(inners, 1, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onNextCrashNoError() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, new Consumer() { + @Override + public void accept(Object t) throws Exception { + throw new IOException(); + } + }, Functions.ON_ERROR_MISSING, () -> { }); + + processor.onNext(1); + + assertTrue(events.toString(), events.isEmpty()); + + TestHelper.assertError(errors, 0, OnErrorNotImplementedException.class); + assertTrue(errors.get(0).getCause() instanceof IOException); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void onCompleteCrash() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose(processor, composite, this, this, new Action() { + @Override + public void run() throws Exception { + throw new IOException(); + } + }); + + processor.onNext(1); + processor.onComplete(); + + assertEquals(Arrays.asList(1), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } + + @Test + public void badSource() { + List errors = TestHelper.trackPluginErrors(); + try { + subscribeAutoDispose( + new Flowable() { + @Override + protected void subscribeActual( + Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + s.onNext(1); + s.onComplete(); + + s.onSubscribe(new BooleanSubscription()); + s.onNext(2); + s.onComplete(); + s.onError(new IOException()); + } + }, composite, this, this, this + ); + + assertEquals(Arrays.asList(1, "OnComplete"), events); + + TestHelper.assertUndeliverable(errors, 0, IOException.class); + } finally { + RxJavaPlugins.reset(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java index fb75eb46b0..56de26e5d6 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/FutureSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class FutureSubscriberTest extends RxJavaTest { @@ -34,7 +34,7 @@ public class FutureSubscriberTest extends RxJavaTest { @Before public void before() { - fs = new FutureSubscriber(); + fs = new FutureSubscriber<>(); } @Test @@ -128,7 +128,7 @@ public void onSubscribe() throws Exception { @Test public void cancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fs = new FutureSubscriber(); + final FutureSubscriber fs = new FutureSubscriber<>(); Runnable r = new Runnable() { @Override @@ -155,9 +155,10 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fs = new FutureSubscriber(); + final FutureSubscriber fs = new FutureSubscriber<>(); final TestException ex = new TestException(); @@ -180,9 +181,10 @@ public void run() { } @Test + @SuppressUndeliverable public void onCompleteCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final FutureSubscriber fs = new FutureSubscriber(); + final FutureSubscriber fs = new FutureSubscriber<>(); if (i % 3 == 0) { fs.onSubscribe(new BooleanSubscription()); @@ -211,6 +213,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorOnComplete() throws Exception { fs.onError(new TestException("One")); fs.onComplete(); @@ -224,6 +227,7 @@ public void onErrorOnComplete() throws Exception { } @Test + @SuppressUndeliverable public void onCompleteOnError() throws Exception { fs.onComplete(); fs.onError(new TestException("One")); @@ -236,6 +240,7 @@ public void onCompleteOnError() throws Exception { } @Test + @SuppressUndeliverable public void cancelOnError() throws Exception { fs.cancel(true); fs.onError(new TestException("One")); @@ -249,6 +254,7 @@ public void cancelOnError() throws Exception { } @Test + @SuppressUndeliverable public void cancelOnComplete() throws Exception { fs.cancel(true); fs.onComplete(); @@ -292,4 +298,20 @@ public void getTimedOut() throws Exception { assertEquals(timeoutMessage(1, TimeUnit.NANOSECONDS), expected.getMessage()); } } + + @Test + public void onNextCompleteOnError() throws Exception { + List errors = TestHelper.trackPluginErrors(); + try { + fs.onNext(1); + fs.onComplete(); + fs.onError(new TestException("One")); + + assertEquals((Integer)1, fs.get(5, TimeUnit.MILLISECONDS)); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + } finally { + RxJavaPlugins.reset(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberTest.java index f613bada98..7856ce5d3e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/InnerQueuedSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,9 +44,9 @@ public void drain() { } }; - InnerQueuedSubscriber inner = new InnerQueuedSubscriber(support, 4); + InnerQueuedSubscriber inner = new InnerQueuedSubscriber<>(support, 4); - final List requests = new ArrayList(); + final List requests = new ArrayList<>(); inner.onSubscribe(new Subscription() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java index 18cde98c75..e7f3931096 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/LambdaSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,26 +28,26 @@ import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class LambdaSubscriberTest extends RxJavaTest { @Test public void onSubscribeThrows() { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaSubscriber subscriber = new LambdaSubscriber(new Consumer() { + LambdaSubscriber subscriber = new LambdaSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -71,20 +71,20 @@ public void accept(Subscription s) throws Exception { @Test public void onNextThrows() { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaSubscriber subscriber = new LambdaSubscriber(new Consumer() { + LambdaSubscriber subscriber = new LambdaSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { throw new TestException(); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -111,20 +111,20 @@ public void onErrorThrows() { List errors = TestHelper.trackPluginErrors(); try { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaSubscriber subscriber = new LambdaSubscriber(new Consumer() { + LambdaSubscriber subscriber = new LambdaSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - throw new TestException("Inner"); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + throw new TestException("Inner"); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -158,20 +158,20 @@ public void onCompleteThrows() { List errors = TestHelper.trackPluginErrors(); try { - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaSubscriber subscriber = new LambdaSubscriber(new Consumer() { + LambdaSubscriber subscriber = new LambdaSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { throw new TestException(); @@ -215,20 +215,20 @@ public void subscribe(Subscriber s) { } }); - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaSubscriber subscriber = new LambdaSubscriber(new Consumer() { + LambdaSubscriber subscriber = new LambdaSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -246,6 +246,7 @@ public void accept(Subscription s) throws Exception { } @Test + @SuppressUndeliverable public void badSourceEmitAfterDone() { Flowable source = Flowable.fromPublisher(new Publisher() { @Override @@ -261,20 +262,20 @@ public void subscribe(Subscriber s) { } }); - final List received = new ArrayList(); + final List received = new ArrayList<>(); - LambdaSubscriber subscriber = new LambdaSubscriber(new Consumer() { + LambdaSubscriber subscriber = new LambdaSubscriber<>(new Consumer() { @Override public void accept(Object v) throws Exception { received.add(v); } }, - new Consumer() { - @Override - public void accept(Throwable e) throws Exception { - received.add(e); - } - }, new Action() { + new Consumer() { + @Override + public void accept(Throwable e) throws Exception { + received.add(e); + } + }, new Action() { @Override public void run() throws Exception { received.add(100); @@ -295,7 +296,7 @@ public void accept(Subscription s) throws Exception { public void onNextThrowsCancelsUpstream() { PublishProcessor pp = PublishProcessor.create(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); pp.subscribe(new Consumer() { @Override @@ -324,9 +325,9 @@ public void accept(Throwable e) throws Exception { public void onSubscribeThrowsCancelsUpstream() { PublishProcessor pp = PublishProcessor.create(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); - pp.subscribe(new LambdaSubscriber(new Consumer() { + pp.subscribe(new LambdaSubscriber<>(new Consumer() { @Override public void accept(Integer v) throws Exception { } @@ -354,7 +355,7 @@ public void accept(Subscription s) throws Exception { @Test public void onErrorMissingShouldReportNoCustomOnError() { - LambdaSubscriber subscriber = new LambdaSubscriber(Functions.emptyConsumer(), + LambdaSubscriber subscriber = new LambdaSubscriber<>(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION, FlowableInternalHelper.RequestMax.INSTANCE); @@ -364,7 +365,7 @@ public void onErrorMissingShouldReportNoCustomOnError() { @Test public void customOnErrorShouldReportCustomOnError() { - LambdaSubscriber subscriber = new LambdaSubscriber(Functions.emptyConsumer(), + LambdaSubscriber subscriber = new LambdaSubscriber<>(Functions.emptyConsumer(), Functions.emptyConsumer(), Functions.EMPTY_ACTION, FlowableInternalHelper.RequestMax.INSTANCE); diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriberTest.java index 74bfb0c626..985705b0e7 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/QueueDrainSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,8 +23,8 @@ import io.reactivex.rxjava3.core.RxJavaTest; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -32,7 +32,7 @@ public class QueueDrainSubscriberTest extends RxJavaTest { static final QueueDrainSubscriber createUnordered(TestSubscriber ts, final Disposable d) { - return new QueueDrainSubscriber(ts, new SpscArrayQueue(4)) { + return new QueueDrainSubscriber(ts, new SpscArrayQueue<>(4)) { @Override public void onNext(Integer t) { fastPathEmitMax(t, false, d); @@ -60,7 +60,7 @@ public boolean accept(Subscriber a, Integer v) { } static final QueueDrainSubscriber createOrdered(TestSubscriber ts, final Disposable d) { - return new QueueDrainSubscriber(ts, new SpscArrayQueue(4)) { + return new QueueDrainSubscriber(ts, new SpscArrayQueue<>(4)) { @Override public void onNext(Integer t) { fastPathOrderedEmitMax(t, false, d); @@ -88,7 +88,7 @@ public boolean accept(Subscriber a, Integer v) { } static final QueueDrainSubscriber createUnorderedReject(TestSubscriber ts, final Disposable d) { - return new QueueDrainSubscriber(ts, new SpscArrayQueue(4)) { + return new QueueDrainSubscriber(ts, new SpscArrayQueue<>(4)) { @Override public void onNext(Integer t) { fastPathEmitMax(t, false, d); @@ -116,7 +116,7 @@ public boolean accept(Subscriber a, Integer v) { } static final QueueDrainSubscriber createOrderedReject(TestSubscriber ts, final Disposable d) { - return new QueueDrainSubscriber(ts, new SpscArrayQueue(4)) { + return new QueueDrainSubscriber(ts, new SpscArrayQueue<>(4)) { @Override public void onNext(Integer t) { fastPathOrderedEmitMax(t, false, d); @@ -145,8 +145,8 @@ public boolean accept(Subscriber a, Integer v) { @Test public void unorderedFastPathNoRequest() { - TestSubscriber ts = new TestSubscriber(0); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(0); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createUnordered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -159,8 +159,8 @@ public void unorderedFastPathNoRequest() { @Test public void orderedFastPathNoRequest() { - TestSubscriber ts = new TestSubscriber(0); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(0); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createOrdered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -173,8 +173,8 @@ public void orderedFastPathNoRequest() { @Test public void acceptBadRequest() { - TestSubscriber ts = new TestSubscriber(0); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(0); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createUnordered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -191,8 +191,8 @@ public void acceptBadRequest() { @Test public void unorderedFastPathRequest1() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createUnordered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -205,8 +205,8 @@ public void unorderedFastPathRequest1() { @Test public void orderedFastPathRequest1() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createOrdered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -219,8 +219,8 @@ public void orderedFastPathRequest1() { @Test public void unorderedSlowPath() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createUnordered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -232,8 +232,8 @@ public void unorderedSlowPath() { @Test public void orderedSlowPath() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createOrdered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -245,8 +245,8 @@ public void orderedSlowPath() { @Test public void orderedSlowPathNonEmptyQueue() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createOrdered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -261,8 +261,8 @@ public void orderedSlowPathNonEmptyQueue() { public void unorderedOnNextRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); final QueueDrainSubscriber qd = createUnordered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -284,8 +284,8 @@ public void run() { public void orderedOnNextRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); final QueueDrainSubscriber qd = createOrdered(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -305,8 +305,8 @@ public void run() { @Test public void unorderedFastPathReject() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createUnorderedReject(ts, d); ts.onSubscribe(new BooleanSubscription()); @@ -321,8 +321,8 @@ public void unorderedFastPathReject() { @Test public void orderedFastPathReject() { - TestSubscriber ts = new TestSubscriber(1); - Disposable d = Disposables.empty(); + TestSubscriber ts = new TestSubscriber<>(1); + Disposable d = Disposable.empty(); QueueDrainSubscriber qd = createOrderedReject(ts, d); ts.onSubscribe(new BooleanSubscription()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriberTest.java index c60ccf8920..8616e6ba3b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/SinglePostCompleteSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public class SinglePostCompleteSubscriberTest extends RxJavaTest { @Test public void requestCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); final SinglePostCompleteSubscriber spc = new SinglePostCompleteSubscriber(ts) { private static final long serialVersionUID = -2848918821531562637L; diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriberTest.java index a94b034166..ba4548d02e 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/StrictSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ public class StrictSubscriberTest extends RxJavaTest { @Test public void strictMode() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Subscriber sub = new Subscriber() { @Override @@ -94,8 +94,8 @@ public void onComplete() { @Test public void normalOnNext() { - TestSubscriberEx ts = new TestSubscriberEx(); - SubscriberWrapper wrapper = new SubscriberWrapper(ts); + TestSubscriberEx ts = new TestSubscriberEx<>(); + SubscriberWrapper wrapper = new SubscriberWrapper<>(ts); Flowable.range(1, 5).subscribe(wrapper); @@ -104,8 +104,8 @@ public void normalOnNext() { @Test public void normalOnNextBackpressured() { - TestSubscriberEx ts = new TestSubscriberEx(0); - SubscriberWrapper wrapper = new SubscriberWrapper(ts); + TestSubscriberEx ts = new TestSubscriberEx<>(0); + SubscriberWrapper wrapper = new SubscriberWrapper<>(ts); Flowable.range(1, 5).subscribe(wrapper); @@ -120,8 +120,8 @@ public void normalOnNextBackpressured() { @Test public void normalOnError() { - TestSubscriberEx ts = new TestSubscriberEx(); - SubscriberWrapper wrapper = new SubscriberWrapper(ts); + TestSubscriberEx ts = new TestSubscriberEx<>(); + SubscriberWrapper wrapper = new SubscriberWrapper<>(ts); Flowable.range(1, 5).concatWith(Flowable.error(new TestException())) .subscribe(wrapper); @@ -131,7 +131,7 @@ public void normalOnError() { @Test public void deferredRequest() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Subscriber sub = new Subscriber() { @Override @@ -163,7 +163,7 @@ public void onComplete() { @Test public void requestZero() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Subscriber sub = new Subscriber() { @Override @@ -195,7 +195,7 @@ public void onComplete() { @Test public void requestNegative() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Subscriber sub = new Subscriber() { @Override @@ -227,7 +227,7 @@ public void onComplete() { @Test public void cancelAfterOnComplete() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Subscriber sub = new Subscriber() { Subscription upstream; @@ -269,7 +269,7 @@ protected void subscribeActual(Subscriber s) { @Test public void cancelAfterOnError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Subscriber sub = new Subscriber() { Subscription upstream; @@ -311,8 +311,8 @@ protected void subscribeActual(Subscriber s) { @Test public void doubleOnSubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); - SubscriberWrapper wrapper = new SubscriberWrapper(ts); + TestSubscriberEx ts = new TestSubscriberEx<>(); + SubscriberWrapper wrapper = new SubscriberWrapper<>(ts); new Flowable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapperTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapperTest.java index 76ed844842..65f2817354 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscribers/SubscriberResourceWrapperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,14 +28,14 @@ public class SubscriberResourceWrapperTest extends RxJavaTest { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - SubscriberResourceWrapper s = new SubscriberResourceWrapper(ts); + SubscriberResourceWrapper s = new SubscriberResourceWrapper<>(ts); @Test public void cancel() { BooleanSubscription bs = new BooleanSubscription(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); s.setResource(d); @@ -54,7 +54,7 @@ public void cancel() { @Test public void error() { BooleanSubscription bs = new BooleanSubscription(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); s.setResource(d); @@ -71,7 +71,7 @@ public void error() { @Test public void complete() { BooleanSubscription bs = new BooleanSubscription(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); s.setResource(d); @@ -94,7 +94,7 @@ public Flowable apply(Flowable f) throws Exception { @Override public Subscriber apply( Subscriber s) throws Exception { - return new SubscriberResourceWrapper(s); + return new SubscriberResourceWrapper<>(s); } }); } @@ -107,7 +107,7 @@ public void badRequest() { @Override public Subscriber apply( Subscriber s) throws Exception { - return new SubscriberResourceWrapper(s); + return new SubscriberResourceWrapper<>(s); } })); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscriptionTest.java index 9dd0cab6a5..fc54cfe357 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ArrayCompositeSubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscriptionTest.java index 30d93a22ae..0375d72ba5 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/AsyncSubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscriptionTest.java index 0f42ba85b6..7ea8d58c3f 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,7 +18,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -26,14 +26,14 @@ public class DeferredScalarSubscriptionTest extends RxJavaTest { @Test public void queueSubscriptionSyncRejected() { - DeferredScalarSubscription ds = new DeferredScalarSubscription(new TestSubscriber()); + DeferredScalarSubscription ds = new DeferredScalarSubscription<>(new TestSubscriber<>()); assertEquals(QueueFuseable.NONE, ds.requestFusion(QueueFuseable.SYNC)); } @Test public void clear() { - DeferredScalarSubscription ds = new DeferredScalarSubscription(new TestSubscriber()); + DeferredScalarSubscription ds = new DeferredScalarSubscription<>(new TestSubscriber<>()); ds.value = 1; @@ -45,7 +45,7 @@ public void clear() { @Test public void cancel() { - DeferredScalarSubscription ds = new DeferredScalarSubscription(new TestSubscriber()); + DeferredScalarSubscription ds = new DeferredScalarSubscription<>(new TestSubscriber<>()); assertTrue(ds.tryCancel()); @@ -55,7 +55,7 @@ public void cancel() { @Test public void completeCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final DeferredScalarSubscription ds = new DeferredScalarSubscription(new TestSubscriber()); + final DeferredScalarSubscription ds = new DeferredScalarSubscription<>(new TestSubscriber<>()); Runnable r1 = new Runnable() { @Override @@ -78,9 +78,9 @@ public void run() { @Test public void requestClearRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); - final DeferredScalarSubscription ds = new DeferredScalarSubscription(ts); + final DeferredScalarSubscription ds = new DeferredScalarSubscription<>(ts); ts.onSubscribe(ds); ds.complete(1); @@ -109,9 +109,9 @@ public void run() { @Test public void requestCancelRace() { for (int i = 0; i < TestHelper.RACE_LONG_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); - final DeferredScalarSubscription ds = new DeferredScalarSubscription(ts); + final DeferredScalarSubscription ds = new DeferredScalarSubscription<>(ts); ts.onSubscribe(ds); ds.complete(1); diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/QueueSubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/QueueSubscriptionTest.java index fb3f66397f..92c37e0e52 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/QueueSubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/QueueSubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscriptionTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscriptionTest.java index 275b15f73c..377691e0a2 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscriptionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/ScalarSubscriptionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,9 +26,9 @@ public class ScalarSubscriptionTest extends RxJavaTest { @Test public void badRequest() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); - ScalarSubscription sc = new ScalarSubscription(ts, 1); + ScalarSubscription sc = new ScalarSubscription<>(ts, 1); List errors = TestHelper.trackPluginErrors(); try { @@ -42,9 +42,9 @@ public void badRequest() { @Test public void noOffer() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); - ScalarSubscription sc = new ScalarSubscription(ts, 1); + ScalarSubscription sc = new ScalarSubscription<>(ts, 1); TestHelper.assertNoOffer(sc); } diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiterTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiterTest.java index 4651b2e3b8..7bc7eb937a 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiterTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionArbiterTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelperTest.java index a28a2dd4e5..9bb222fbb8 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/subscriptions/SubscriptionHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -54,7 +54,7 @@ public void cancelNoOp() { @Test public void set() { - AtomicReference atomicSubscription = new AtomicReference(); + AtomicReference atomicSubscription = new AtomicReference<>(); BooleanSubscription bs1 = new BooleanSubscription(); @@ -71,7 +71,7 @@ public void set() { @Test public void replace() { - AtomicReference atomicSubscription = new AtomicReference(); + AtomicReference atomicSubscription = new AtomicReference<>(); BooleanSubscription bs1 = new BooleanSubscription(); @@ -89,7 +89,7 @@ public void replace() { @Test public void cancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference atomicSubscription = new AtomicReference(); + final AtomicReference atomicSubscription = new AtomicReference<>(); Runnable r = new Runnable() { @Override @@ -105,7 +105,7 @@ public void run() { @Test public void setRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference atomicSubscription = new AtomicReference(); + final AtomicReference atomicSubscription = new AtomicReference<>(); final BooleanSubscription bs1 = new BooleanSubscription(); final BooleanSubscription bs2 = new BooleanSubscription(); @@ -133,7 +133,7 @@ public void run() { @Test public void replaceRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference atomicSubscription = new AtomicReference(); + final AtomicReference atomicSubscription = new AtomicReference<>(); final BooleanSubscription bs1 = new BooleanSubscription(); final BooleanSubscription bs2 = new BooleanSubscription(); @@ -161,7 +161,7 @@ public void run() { @Test public void cancelAndChange() { - AtomicReference atomicSubscription = new AtomicReference(); + AtomicReference atomicSubscription = new AtomicReference<>(); SubscriptionHelper.cancel(atomicSubscription); @@ -180,7 +180,7 @@ public void cancelAndChange() { @Test public void invalidDeferredRequest() { - AtomicReference atomicSubscription = new AtomicReference(); + AtomicReference atomicSubscription = new AtomicReference<>(); AtomicLong r = new AtomicLong(); List errors = TestHelper.trackPluginErrors(); @@ -196,7 +196,7 @@ public void invalidDeferredRequest() { @Test public void deferredRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference atomicSubscription = new AtomicReference(); + final AtomicReference atomicSubscription = new AtomicReference<>(); final AtomicLong r = new AtomicLong(); final AtomicLong q = new AtomicLong(); @@ -237,7 +237,7 @@ public void run() { @Test public void setOnceAndRequest() { - AtomicReference ref = new AtomicReference(); + AtomicReference ref = new AtomicReference<>(); Subscription sub = mock(Subscription.class); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/AtomicThrowableTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/AtomicThrowableTest.java index 86f823e22e..152039fac3 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/AtomicThrowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/AtomicThrowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,10 +17,10 @@ import java.util.List; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; @@ -91,7 +91,7 @@ public void tryTerminateAndReportHasError() { @Test public void tryTerminateConsumerSubscriberNoError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); AtomicThrowable ex = new AtomicThrowable(); @@ -101,7 +101,7 @@ public void tryTerminateConsumerSubscriberNoError() { @Test public void tryTerminateConsumerSubscriberError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); AtomicThrowable ex = new AtomicThrowable(); @@ -112,7 +112,7 @@ public void tryTerminateConsumerSubscriberError() { @Test public void tryTerminateConsumerSubscriberTerminated() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); AtomicThrowable ex = new AtomicThrowable(); @@ -123,8 +123,8 @@ public void tryTerminateConsumerSubscriberTerminated() { @Test public void tryTerminateConsumerObserverNoError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.tryTerminateConsumer((Observer)to); @@ -133,8 +133,8 @@ public void tryTerminateConsumerObserverNoError() { @Test public void tryTerminateConsumerObserverError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.set(new TestException()); @@ -144,8 +144,8 @@ public void tryTerminateConsumerObserverError() { @Test public void tryTerminateConsumerObserverTerminated() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.terminate(); @@ -155,8 +155,8 @@ public void tryTerminateConsumerObserverTerminated() { @Test public void tryTerminateConsumerMaybeObserverNoError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.tryTerminateConsumer((MaybeObserver)to); @@ -165,8 +165,8 @@ public void tryTerminateConsumerMaybeObserverNoError() { @Test public void tryTerminateConsumerMaybeObserverError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.set(new TestException()); @@ -176,8 +176,8 @@ public void tryTerminateConsumerMaybeObserverError() { @Test public void tryTerminateConsumerMaybeObserverTerminated() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.terminate(); @@ -187,8 +187,8 @@ public void tryTerminateConsumerMaybeObserverTerminated() { @Test public void tryTerminateConsumerSingleNoError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.tryTerminateConsumer((SingleObserver)to); @@ -197,8 +197,8 @@ public void tryTerminateConsumerSingleNoError() { @Test public void tryTerminateConsumerSingleError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.set(new TestException()); @@ -208,8 +208,8 @@ public void tryTerminateConsumerSingleError() { @Test public void tryTerminateConsumerSingleTerminated() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.terminate(); @@ -219,8 +219,8 @@ public void tryTerminateConsumerSingleTerminated() { @Test public void tryTerminateConsumerCompletableObserverNoError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.tryTerminateConsumer((CompletableObserver)to); @@ -229,8 +229,8 @@ public void tryTerminateConsumerCompletableObserverNoError() { @Test public void tryTerminateConsumerCompletableObserverError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.set(new TestException()); @@ -240,8 +240,8 @@ public void tryTerminateConsumerCompletableObserverError() { @Test public void tryTerminateConsumerCompletableObserverTerminated() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.terminate(); @@ -270,8 +270,8 @@ public void onComplete() { @Test public void tryTerminateConsumerEmitterNoError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.tryTerminateConsumer(wrapToEmitter(to)); @@ -280,8 +280,8 @@ public void tryTerminateConsumerEmitterNoError() { @Test public void tryTerminateConsumerEmitterError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.set(new TestException()); @@ -291,8 +291,8 @@ public void tryTerminateConsumerEmitterError() { @Test public void tryTerminateConsumerEmitterTerminated() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); AtomicThrowable ex = new AtomicThrowable(); ex.terminate(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/BackpressureHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/BackpressureHelperTest.java index ca0b0e2668..d13cde57be 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/BackpressureHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/BackpressureHelperTest.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.util; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/BlockingHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/BlockingHelperTest.java index 7d4e07dab4..0218185f08 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/BlockingHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/BlockingHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ public void emptyEnum() { @Test public void interrupted() { CountDownLatch cdl = new CountDownLatch(1); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); Thread.currentThread().interrupt(); @@ -50,7 +50,7 @@ public void interrupted() { @Test public void unblock() { final CountDownLatch cdl = new CountDownLatch(1); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); Schedulers.computation().scheduleDirect(new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/CrashingIterable.java b/src/test/java/io/reactivex/rxjava3/internal/util/CrashingIterable.java index bb053c23b0..6bdbea27b1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/CrashingIterable.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/CrashingIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/CrashingMappedIterable.java b/src/test/java/io/reactivex/rxjava3/internal/util/CrashingMappedIterable.java index 53089322ea..d6b9491e2b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/CrashingMappedIterable.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/CrashingMappedIterable.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,7 +45,7 @@ public Iterator iterator() { if (--crashOnIterator <= 0) { throw new TestException("iterator()"); } - return new CrashingMapperIterator(crashOnHasNext, crashOnNext, mapper); + return new CrashingMapperIterator<>(crashOnHasNext, crashOnNext, mapper); } static final class CrashingMapperIterator implements Iterator { diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/EndConsumerHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/EndConsumerHelperTest.java index 0ff03f24c2..8ad1942946 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/EndConsumerHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/EndConsumerHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -209,13 +209,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -244,13 +244,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -279,13 +279,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -310,13 +310,13 @@ public void onError(Throwable t) { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -341,13 +341,13 @@ public void onError(Throwable t) { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -376,13 +376,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -411,13 +411,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -442,13 +442,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -473,13 +473,13 @@ public void onComplete() { } }; - Disposable sub1 = Disposables.empty(); + Disposable sub1 = Disposable.empty(); consumer.onSubscribe(sub1); assertFalse(sub1.isDisposed()); - Disposable sub2 = Disposables.empty(); + Disposable sub2 = Disposable.empty(); consumer.onSubscribe(sub2); @@ -494,7 +494,7 @@ public void onComplete() { @Test public void validateDisposable() { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); assertFalse(EndConsumerHelper.validate(DisposableHelper.DISPOSED, d1, getClass())); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/ExceptionHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/ExceptionHelperTest.java index 666718a1f5..130cf5767b 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/ExceptionHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/ExceptionHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public void utilityClass() { public void addRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); final TestException ex = new TestException(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java index 2171fadc2d..ea8c405849 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.util; import static org.junit.Assert.assertTrue; @@ -64,7 +65,7 @@ public void onComplete() { a[0] = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); HalfSerializer.onNext(observer, 1, wip, error); @@ -108,7 +109,7 @@ public void onComplete() { a[0] = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); HalfSerializer.onNext(observer, 1, wip, error); @@ -152,7 +153,7 @@ public void onComplete() { a[0] = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); HalfSerializer.onNext(observer, 1, wip, error); @@ -160,6 +161,7 @@ public void onComplete() { } @Test + @SuppressUndeliverable @SuppressWarnings({ "rawtypes", "unchecked" }) public void reentrantErrorOnError() { final AtomicInteger wip = new AtomicInteger(); @@ -194,7 +196,7 @@ public void onComplete() { a[0] = observer; - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); HalfSerializer.onError(observer, new TestException(), wip, error); @@ -208,8 +210,8 @@ public void onNextOnCompleteRace() { final AtomicInteger wip = new AtomicInteger(); final AtomicThrowable error = new AtomicThrowable(); - final TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + final TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); Runnable r1 = new Runnable() { @Override @@ -234,15 +236,16 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorOnCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final AtomicInteger wip = new AtomicInteger(); final AtomicThrowable error = new AtomicThrowable(); - final TestObserverEx to = new TestObserverEx(); + final TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); final TestException ex = new TestException(); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java index d7c472e660..1919864648 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/HalfSerializerSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.internal.util; import static org.junit.Assert.assertTrue; @@ -166,6 +167,7 @@ public void onComplete() { } @Test + @SuppressUndeliverable @SuppressWarnings({ "rawtypes", "unchecked" }) public void reentrantErrorOnError() { final AtomicInteger wip = new AtomicInteger(); @@ -214,7 +216,7 @@ public void onNextOnCompleteRace() { final AtomicInteger wip = new AtomicInteger(); final AtomicThrowable error = new AtomicThrowable(); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); Runnable r1 = new Runnable() { @@ -240,13 +242,14 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorOnCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final AtomicInteger wip = new AtomicInteger(); final AtomicThrowable error = new AtomicThrowable(); - final TestSubscriberEx ts = new TestSubscriberEx(); + final TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/JavadocNoThrows.java b/src/test/java/io/reactivex/rxjava3/internal/util/JavadocNoThrows.java new file mode 100644 index 0000000000..1a55807dd0 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/util/JavadocNoThrows.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.util; + +import java.io.File; +import java.nio.file.Files; +import java.util.List; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Scan the JavaDocs of the base classes and list those which do not have the {@code @throws} tag. + * The lack is not an error by itself but worth looking at. + */ +public final class JavadocNoThrows { + + private JavadocNoThrows() { + throw new IllegalArgumentException("No instances!"); + } + + public static void main(String[] args) throws Exception { + for (Class clazz : CLASSES) { + String clazzName = clazz.getSimpleName(); + String packageName = clazz.getPackage().getName(); + File f = TestHelper.findSource(clazzName, packageName); + + List lines = Files.readAllLines(f.toPath()); + + for (int i = 1; i < lines.size(); i++) { + String line = lines.get(i).trim(); + + if (line.startsWith("/**")) { + boolean found = false; + for (int j = i + 1; j < lines.size(); j++) { + + String line2 = lines.get(j).trim(); + if (line2.startsWith("public")) { + if (line2.endsWith("() {")) { + found = true; + } + break; + } + if (line2.startsWith("* @throws")) { + found = true; + break; + } + } + + if (!found) { + System.out.printf(" at %s.%s.method(%s.java:%s)%n%n", packageName, clazzName, clazzName, i + 1); + } + } + } + } + } + + static final Class[] CLASSES = { + Flowable.class, Observable.class, Maybe.class, Single.class, Completable.class, ParallelFlowable.class + }; +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/MarbleDimensions.java b/src/test/java/io/reactivex/rxjava3/internal/util/MarbleDimensions.java new file mode 100644 index 0000000000..2fdc7fe2d6 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/util/MarbleDimensions.java @@ -0,0 +1,107 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.util; + +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; +import java.nio.file.Files; +import java.util.*; +import java.util.regex.*; + +import javax.imageio.ImageIO; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Parses the main sources, locates the {@code } tags, downloads + * the referenced image and checks if the scaled dimensions are correct. + */ +public final class MarbleDimensions { + + /** Helper program. */ + private MarbleDimensions() { + throw new IllegalStateException("No instances!"); + } + + public static void main(String[] args) throws Throwable { + Pattern p = Pattern.compile("\\s*\\*\\s*\\ dimensions = new HashMap<>(); + + for (Class clazz : CLASSES) { + String simpleName = clazz.getSimpleName(); + System.out.println(simpleName); + System.out.println("----"); + String packageName = clazz.getPackage().getName(); + + File f = TestHelper.findSource(clazz.getSimpleName(), packageName); + if (f == null) { + System.err.println("Unable to locate " + clazz); + continue; + } + + List lines = Files.readAllLines(f.toPath()); + + for (int i = 0; i < lines.size(); i++) { + Matcher m = p.matcher(lines.get(i)); + if (m.matches()) { + int width = Integer.parseInt(m.group(2)); + int height = Integer.parseInt(m.group(5)); + String url = m.group(8); + + Integer[] imageDim = dimensions.get(url); + if (imageDim == null) { + Thread.sleep(SLEEP_PER_IMAGE_MILLIS); + + try { + BufferedImage bimg = ImageIO.read(new URL(url)); + + if (bimg == null) { + throw new IOException("not found"); + } + imageDim = new Integer[] { 0, 0 }; + imageDim[0] = bimg.getWidth(); + imageDim[1] = bimg.getHeight(); + + dimensions.put(url, imageDim); + } catch (IOException ex) { + System.err.printf("%s => %s%n", url, ex); + System.err.printf(" at %s.%s.method(%s.java:%d)%n", packageName, simpleName, simpleName, i + 1); + } + } + + if (imageDim != null) { + int expectedHeight = (int)Math.round(1.0 * width / imageDim[0] * imageDim[1]); + + if (expectedHeight != height) { + System.out.printf(" %d => %d%n", height, expectedHeight); + System.out.printf(" at %s.%s.method(%s.java:%d)%n", packageName, simpleName, simpleName, i + 1); + } + } + // System.out.printf("%d: %d x %d => %s%n", i + 1, width, height, url); + } + } + } + } + + static final int SLEEP_PER_IMAGE_MILLIS = 25; + + static final Class[] CLASSES = { + Flowable.class, Observable.class, Maybe.class, Single.class, Completable.class, ParallelFlowable.class + }; +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/MergerBiFunctionTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/MergerBiFunctionTest.java index c8a0968729..b8a5f8b843 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/MergerBiFunctionTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/MergerBiFunctionTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public class MergerBiFunctionTest extends RxJavaTest { @Test public void firstEmpty() throws Exception { - MergerBiFunction merger = new MergerBiFunction(new Comparator() { + MergerBiFunction merger = new MergerBiFunction<>(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); @@ -38,7 +38,7 @@ public int compare(Integer o1, Integer o2) { @Test public void bothEmpty() throws Exception { - MergerBiFunction merger = new MergerBiFunction(new Comparator() { + MergerBiFunction merger = new MergerBiFunction<>(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); @@ -51,7 +51,7 @@ public int compare(Integer o1, Integer o2) { @Test public void secondEmpty() throws Exception { - MergerBiFunction merger = new MergerBiFunction(new Comparator() { + MergerBiFunction merger = new MergerBiFunction<>(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); @@ -64,7 +64,7 @@ public int compare(Integer o1, Integer o2) { @Test public void sameSize() throws Exception { - MergerBiFunction merger = new MergerBiFunction(new Comparator() { + MergerBiFunction merger = new MergerBiFunction<>(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); @@ -77,7 +77,7 @@ public int compare(Integer o1, Integer o2) { @Test public void sameSizeReverse() throws Exception { - MergerBiFunction merger = new MergerBiFunction(new Comparator() { + MergerBiFunction merger = new MergerBiFunction<>(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java index d0b627afaa..adc779a302 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/MiscUtilTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -60,21 +60,25 @@ public void errorModeEnum() { @Test public void linkedArrayList() { LinkedArrayList list = new LinkedArrayList(2); + assertEquals(0, list.size()); list.add(1); + assertEquals(1, list.size()); list.add(2); + assertEquals(2, list.size()); list.add(3); + assertEquals(3, list.size()); assertEquals("[1, 2, 3]", list.toString()); } @Test public void appendOnlyLinkedArrayListForEachWhile() throws Exception { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(2); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(2); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(new NonThrowingPredicate() { @Override @@ -89,13 +93,13 @@ public boolean test(Integer t2) { @Test public void appendOnlyLinkedArrayListForEachWhileBi() throws Throwable { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(2); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(2); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(2, new BiPredicate() { @Override @@ -110,13 +114,13 @@ public boolean test(Integer t1, Integer t2) throws Throwable { @Test public void appendOnlyLinkedArrayListForEachWhilePreGrow() throws Exception { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(12); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(12); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(new NonThrowingPredicate() { @Override @@ -131,13 +135,13 @@ public boolean test(Integer t2) { @Test public void appendOnlyLinkedArrayListForEachWhileExact() throws Exception { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(3); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(3); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(new NonThrowingPredicate() { @Override @@ -152,13 +156,13 @@ public boolean test(Integer t2) { @Test public void appendOnlyLinkedArrayListForEachWhileAll() throws Exception { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(2); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(2); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(new NonThrowingPredicate() { @Override @@ -173,13 +177,13 @@ public boolean test(Integer t2) { @Test public void appendOnlyLinkedArrayListForEachWhileBigger() throws Exception { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(4); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(4); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(new NonThrowingPredicate() { @Override @@ -194,13 +198,13 @@ public boolean test(Integer t2) { @Test public void appendOnlyLinkedArrayListForEachWhileBiPreGrow() throws Throwable { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(12); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(12); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(2, new BiPredicate() { @Override @@ -215,13 +219,13 @@ public boolean test(Integer t1, Integer t2) throws Throwable { @Test public void appendOnlyLinkedArrayListForEachWhileBiExact() throws Throwable { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(3); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(3); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(2, new BiPredicate() { @Override @@ -236,13 +240,13 @@ public boolean test(Integer t1, Integer t2) throws Exception { @Test public void appendOnlyLinkedArrayListForEachWhileBiAll() throws Throwable { - AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList(2); + AppendOnlyLinkedArrayList list = new AppendOnlyLinkedArrayList<>(2); list.add(1); list.add(2); list.add(3); - final List out = new ArrayList(); + final List out = new ArrayList<>(); list.forEachWhile(3, new BiPredicate() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/NotificationLiteTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/NotificationLiteTest.java index 0149b14f4e..7638a98ead 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/NotificationLiteTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/NotificationLiteTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,9 +26,9 @@ public class NotificationLiteTest extends RxJavaTest { @Test public void acceptFullObserver() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); assertFalse(NotificationLite.acceptFull(NotificationLite.disposable(d), to)); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/ObservableToFlowabeTestSync.java b/src/test/java/io/reactivex/rxjava3/internal/util/ObservableToFlowabeTestSync.java index 02b64e2772..7bf668e2f1 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/ObservableToFlowabeTestSync.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/ObservableToFlowabeTestSync.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ private ObservableToFlowabeTestSync() { } static List readAllLines(File f) { - List result = new ArrayList(); + List result = new ArrayList<>(); try { BufferedReader in = new BufferedReader(new FileReader(f)); try { @@ -65,7 +65,7 @@ static void list(String basepath, String basepackage) throws Exception { Class clazz2 = Class.forName(basepackage + "flowable." + cn); - Set methods2 = new HashSet(); + Set methods2 = new HashSet<>(); for (Method m : clazz2.getMethods()) { methods2.add(m.getName()); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/OpenHashSetTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/OpenHashSetTest.java index e424298e46..f8c1bdf04d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/OpenHashSetTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/OpenHashSetTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public void addRemoveCollision() { Value v1 = new Value(); Value v2 = new Value(); - OpenHashSet set = new OpenHashSet(); + OpenHashSet set = new OpenHashSet<>(); assertTrue(set.add(v1)); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/OperatorArgumentNaming.java b/src/test/java/io/reactivex/rxjava3/internal/util/OperatorArgumentNaming.java new file mode 100644 index 0000000000..1814a26bbc --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/util/OperatorArgumentNaming.java @@ -0,0 +1,169 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.util; + +import java.lang.reflect.*; +import java.util.*; + +import org.reactivestreams.*; + +import com.google.common.base.Strings; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.core.Observable; + +/** + * Compare method argument naming across base classes. + * This is not a full test because some naming mismatch is legitimate, such as singular in Maybe/Single and + * plural in Flowable/Observable + */ +public final class OperatorArgumentNaming { + + private OperatorArgumentNaming() { + throw new IllegalStateException("No instances!"); + } + + /** Classes to compare with each other. */ + static final Class[] CLASSES = { Flowable.class, Observable.class, Maybe.class, Single.class, Completable.class }; + + /** Types that refer to a reactive type and is generally matching the parent class; for comparison, these have to be unified. */ + static final Set> BASE_TYPE_SET = new HashSet<>(Arrays.asList( + Flowable.class, Publisher.class, Subscriber.class, FlowableSubscriber.class, + Observable.class, ObservableSource.class, Observer.class, + Maybe.class, MaybeSource.class, MaybeObserver.class, + Single.class, SingleSource.class, SingleObserver.class, + Completable.class, CompletableSource.class, CompletableObserver.class + )); + + public static void main(String[] args) { + // className -> methodName -> overloads -> arguments + Map>>> map = new HashMap<>(); + + for (Class clazz : CLASSES) { + Map>> classMethods = map.computeIfAbsent(clazz.getSimpleName(), v -> new HashMap<>()); + for (Method method : clazz.getDeclaredMethods()) { + if (method.getDeclaringClass() == clazz && method.getParameterCount() != 0) { + List> overloads = classMethods.computeIfAbsent(method.getName(), v -> new ArrayList<>()); + + List overload = new ArrayList<>(); + overloads.add(overload); + + for (Parameter param : method.getParameters()) { + String typeName; + Class type = param.getType(); + if (type.isArray()) { + Class componentType = type.getComponentType(); + if (BASE_TYPE_SET.contains(componentType)) { + typeName = "BaseType"; + } else { + typeName = type.getComponentType().getSimpleName() + "[]"; + } + } else + if (BASE_TYPE_SET.contains(type)) { + typeName = "BaseType"; + } else { + typeName = type.getSimpleName(); + } + String name = param.getName(); + if (name.equals("bufferSize") || name.equals("prefetch") || name.equals("capacityHint")) { + name = "bufferSize|prefetch|capacityHint"; + } + if (name.equals("subscriber") || name.equals("observer")) { + name = "subscriber|observer"; + } + if (name.contains("onNext")) { + name = name.replace("onNext", "onNext|onSuccess"); + } else + if (name.contains("onSuccess")) { + name = name.replace("onSuccess", "onNext|onSuccess"); + } + overload.add(new ArgumentNameAndType(typeName, name)); + } + } + } + } + + int counter = 0; + + for (int i = 0; i < CLASSES.length - 1; i++) { + String firstName = CLASSES[i].getSimpleName(); + Map>> firstClassMethods = map.get(firstName); + for (int j = i + 1; j < CLASSES.length; j++) { + String secondName = CLASSES[j].getSimpleName(); + Map>> secondClassMethods = map.get(secondName); + + for (Map.Entry>> methodOverloadsFirst : firstClassMethods.entrySet()) { + + List> methodOverloadsSecond = secondClassMethods.get(methodOverloadsFirst.getKey()); + + if (methodOverloadsSecond != null) { + for (List overloadFirst : methodOverloadsFirst.getValue()) { + for (List overloadSecond : methodOverloadsSecond) { + if (overloadFirst.size() == overloadSecond.size()) { + // Argument types match? + boolean match = true; + for (int k = 0; k < overloadFirst.size(); k++) { + if (!overloadFirst.get(k).type.equals(overloadSecond.get(k).type)) { + match = false; + break; + } + } + // Argument names match? + if (match) { + for (int k = 0; k < overloadFirst.size(); k++) { + if (!overloadFirst.get(k).name.equals(overloadSecond.get(k).name)) { + System.out.print("Argument naming mismatch #"); + System.out.println(++counter); + + System.out.print(" "); + System.out.print(Strings.padEnd(firstName, Math.max(firstName.length(), secondName.length()) + 1, ' ')); + System.out.print(methodOverloadsFirst.getKey()); + System.out.print(" "); + System.out.println(overloadFirst); + + System.out.print(" "); + System.out.print(Strings.padEnd(secondName, Math.max(firstName.length(), secondName.length()) + 1, ' ')); + System.out.print(methodOverloadsFirst.getKey()); + System.out.print(" "); + System.out.println(overloadSecond); + System.out.println(); + break; + } + } + } + } + } + } + } + } + } + } + } + + static final class ArgumentNameAndType { + final String type; + final String name; + + ArgumentNameAndType(String type, String name) { + this.type = type; + this.name = name; + } + + @Override + public String toString() { + return type + " " + name; + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/OperatorMatrixGenerator.java b/src/test/java/io/reactivex/rxjava3/internal/util/OperatorMatrixGenerator.java new file mode 100644 index 0000000000..9c5f058ff4 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/internal/util/OperatorMatrixGenerator.java @@ -0,0 +1,522 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.internal.util; + +import java.io.*; +import java.lang.reflect.Method; +import java.nio.file.*; +import java.util.*; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; + +/** + * Generate a table of available operators across base classes in {@code Operator-Matrix.md}. + * + * Should be run with the main project directory as working directory where the {@code docs} + * folder is. + */ +public final class OperatorMatrixGenerator { + + private OperatorMatrixGenerator() { + throw new IllegalStateException("No instances!"); + } + + static final String PRESENT = "![present](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_on.png)"; + static final String ABSENT = "![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_off.png)"; + static final String TBD = "![absent](https://raw.github.com/wiki/ReactiveX/RxJava/images/checkmark_half.png)"; + + static final Class[] CLASSES = { + Flowable.class, Observable.class, Maybe.class, Single.class, Completable.class + }; + + static String header(String type) { + return "![" + type + "](https://raw.github.com/wiki/ReactiveX/RxJava/images/opmatrix-" + type.toLowerCase() + ".png)"; + } + + public static void main(String[] args) throws IOException { + Set operatorSet = new HashSet<>(); + Map, Set> operatorMap = new HashMap<>(); + + for (Class clazz : CLASSES) { + Set set = operatorMap.computeIfAbsent(clazz, c -> new HashSet<>()); + + for (Method m : clazz.getMethods()) { + String name = m.getName(); + if (!name.equals("bufferSize") + && m.getDeclaringClass() == clazz + && !m.isSynthetic()) { + operatorSet.add(m.getName()); + set.add(m.getName()); + } + } + } + + List sortedOperators = new ArrayList<>(operatorSet); + sortedOperators.sort(Comparator.naturalOrder()); + + try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get("docs", "Operator-Matrix.md"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) { + out.print("Operator |"); + for (Class clazz : CLASSES) { + out.print(" "); + out.print(header(clazz.getSimpleName())); + out.print(" |"); + } + out.println(); + out.print("-----|"); + for (int i = 0; i < CLASSES.length; i++) { + out.print("---|"); + } + out.println(); + + Map notesMap = new HashMap<>(); + List notesList = new ArrayList<>(); + List tbdList = new ArrayList<>(); + int[] counters = new int[CLASSES.length]; + + for (String operatorName : sortedOperators) { + out.print("`"); + out.print(operatorName); + out.print("`|"); + int m = 0; + for (Class clazz : CLASSES) { + if (operatorMap.get(clazz).contains(operatorName)) { + out.print(PRESENT); + counters[m]++; + } else { + String notes = findNotes(clazz.getSimpleName(), operatorName); + if (notes != null) { + out.print(ABSENT); + Integer index = notesMap.get(notes); + if (index == null) { + index = notesMap.size() + 1; + notesMap.put(notes, index); + notesList.add(notes); + } + out.print(" (["); + out.print(index); + out.print("](#notes-"); + out.print(index); + out.print("))"); + } else { + out.print(TBD); + tbdList.add(clazz.getSimpleName() + "." + operatorName + "()"); + } + } + out.print("|"); + m++; + } + out.println(); + } + out.print("**"); + out.print(sortedOperators.size()); + out.print(" operators** |"); + for (int m = 0; m < counters.length; m++) { + out.print(" **"); + out.print(counters[m]); + out.print("** |"); + } + out.println(); + + if (!notesList.isEmpty()) { + out.println(); + out.println("#### Notes"); + + for (int i = 0; i < notesList.size(); i++) { + out.print(""); + out.print(i + 1); + out.print(" "); + out.print(notesList.get(i)); + out.println("
    "); + } + } + if (tbdList.isEmpty()) { + out.println(); + out.println("#### Under development"); + out.println(); + out.println("*Currently, all intended operators are implemented.*"); + } else { + out.println(); + out.println("#### Under development"); + out.println(); + + for (int i = 0; i < tbdList.size(); i++) { + out.print(i + 1); + out.print(". "); + out.println(tbdList.get(i)); + } + } + } + } + + static String findNotes(String clazzName, String operatorName) { + Map classNotes = NOTES_MAP.get(operatorName); + if (classNotes != null) { + return classNotes.get(clazzName.substring(0, 1)); + } + switch (operatorName) { + case "empty": { + if ("Completable".equals(clazzName)) { + return "Use [`complete()`](#complete)."; + } + if ("Single".equals(clazzName)) { + return "Never empty."; + } + break; + } + } + return null; + } + + static final String[] NOTES = { + // Format + // FOMSC methodName note + " MS all Use [`contains()`](#contains).", + " C all Always empty.", + "FOMS andThen Use [`concatWith`](#concatWith).", + " MS any Use [`contains()`](#contains).", + " C any Always empty.", + "FO blockingAwait Use [`blockingFirst()`](#blockingFirst), [`blockingSingle()`](#blockingSingle) or [`blockingLast()`](#blockingLast).", + " MS blockingAwait Use [`blockingGet()`](#blockingGet).", + " MS blockingFirst At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingFirst No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MSC blockingForEach Use [`blockingSubscribe()`](#blockingSubscribe)", + "FO blockingGet Use [`blockingFirst()`](#blockingFirst), [`blockingSingle()`](#blockingSingle) or [`blockingLast()`](#blockingLast).", + " C blockingGet No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingIterable At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingIterable No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingLast At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingLast No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingLatest At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingLatest No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingMostRecent At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingMostRecent No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingNext At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingNext No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingSingle At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingSingle No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " MS blockingStream At most one element to get. Use [`blockingGet()`](#blockingGet).", + " C blockingStream No elements to get. Use [`blockingAwait()`](#blockingAwait).", + " M buffer Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S buffer Use [`map()`](#map) to transform into a list/collection.", + " C buffer Always empty. Use [`andThen()`](#andThen) to bring in a list/collection.", + " MSC cacheWithInitialCapacity At most one element to store. Use [`cache()`](#cache).", + " C cast Always empty.", + " M collect At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S collect One element to collect. Use [`map()`](#map) to transform into a list/collection.", + " C collect Always empty. Use [`andThen()`](#andThen) to bring in a collection.", + " M collectInto At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S collectInto One element to collect. Use [`map()`](#map) to transform into a list/collection.", + " C collectInto Always empty. Use [`andThen()`](#andThen) to bring in a collection.", + " MS combineLatest At most one element per source. Use [`zip()`](#zip).", + " C combineLatest Always empty. Use [`merge()`](#merge).", + " MS combineLatestArray At most one element per source. Use [`zipArray()`](#zipArray).", + " C combineLatestArray Always empty. Use [`mergeArray()`](#mergeArray).", + " MS combineLatestDelayError At most one element per source. Use [`zip()`](#zip).", + " C combineLatestDelayError Always empty. Use [`mergeDelayError()`](#mergeDelayError).", + " MS combineLatestArrayDelayError At most one element per source. Use [`zipArray()`](#zipArray).", + " C combineLatestArrayDelayError Always empty. Use [`mergeArrayDelayError()`](#mergeArrayDelayError).", + "FOM complete Use [`empty()`](#empty).", + " S complete Never empty.", + " C concatArrayEager No items to keep ordered. Use [`mergeArray()`](#mergeArray).", + " C concatArrayEagerDelayError No items to keep ordered. Use [`mergeArrayDelayError()`](#mergeArrayDelayError).", + " C concatEager No items to keep ordered. Use [`merge()`](#merge).", + " C concatEagerDelayError No items to keep ordered. Use [`mergeDelayError()`](#mergeDelayError).", + " C concatMap Always empty thus no items to map.", + " C concatMapCompletable Always empty thus no items to map.", + " MS concatMapCompletableDelayError Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapCompletable`](#concatMapCompletable).", + " C concatMapCompletableDelayError Always empty thus no items to map.", + " MS concatMapDelayError Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMap`](#concatMap).", + " C concatMapDelayError Always empty thus no items to map.", + " MS concatMapEager At most one item to map. Use [`concatMap()`](#concatMap).", + " C concatMapEager Always empty thus no items to map.", + " MS concatMapEagerDelayError At most one item to map. Use [`concatMap()`](#concatMap).", + " C concatMapEagerDelayError Always empty thus no items to map.", + " C concatMapIterable Always empty thus no items to map.", + " M concatMapMaybe Use [`concatMap`](#concatMap).", + " C concatMapMaybe Always empty thus no items to map.", + " MS concatMapMaybeDelayError Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapMaybe`](#concatMapMaybe).", + " C concatMapMaybeDelayError Always empty thus no items to map.", + " S concatMapSingle Use [`concatMap()`](#concatMap).", + " C concatMapSingle Always empty thus no items to map.", + " MS concatMapSingleDelayError Either the upstream fails (thus no inner) or the mapped-in source, but never both. Use [`concatMapSingle`](#concatMapSingle).", + " C concatMapSingleDelayError Always empty thus no items to map.", + " C concatMapStream Always empty thus no items to map.", + " MS concatMapIterable At most one item. Use [`flattenAsFlowable`](#flattenAsFlowable) or [`flattenAsObservable`](#flattenAsObservable).", + " MS concatMapStream At most one item. Use [`flattenStreamAsFlowable`](#flattenStreamAsFlowable) or [`flattenStreamAsObservable`](#flattenStreamAsObservable).", + " C contains Always empty.", + " S count Never empty thus always 1.", + " C count Always empty thus always 0.", + " MS debounce At most one item signaled so no subsequent items to work with.", + " C debounce Always empty thus no items to work with.", + " S defaultIfEmpty Never empty.", + " C defaultIfEmpty Always empty. Use [`andThen()`](#andThen) to chose the follow-up sequence.", + " C dematerialize Always empty thus no items to work with.", + " MS distinct At most one item, always distinct.", + " C distinct Always empty thus no items to work with.", + " MS distinctUntilChanged At most one item, always distinct.", + " C distinctUntilChanged Always empty thus no items to work with.", + " MS doAfterNext Different terminology. Use [`doAfterSuccess()`](#doAfterSuccess).", + " C doAfterNext Always empty.", + "FO doAfterSuccess Different terminology. Use [`doAfterNext()`](#doAfterNext).", + " C doAfterSuccess Always empty thus no items to work with.", + " OMSC doOnCancel Different terminology. Use [`doOnDispose()`](#doOnDispose).", + " S doOnComplete Always succeeds or fails, there is no `onComplete` signal.", + "F doOnDispose Different terminology. Use [`doOnCancel()`](#doOnCancel).", + " MS doOnEach At most one item. Use [`doOnEvent()`](#doOnEvent).", + " C doOnEach Always empty thus no items to work with.", + "FO doOnEvent Use [`doOnEach()`](#doOnEach).", + " MS doOnNext Different terminology. Use [`doOnSuccess()`](#doOnSuccess).", + " C doOnNext Always empty thus no items to work with.", + " OMSC doOnRequest Backpressure related and not supported outside `Flowable`.", + "FO doOnSuccess Different terminology. Use [`doOnNext()`](#doOnNext).", + " C doOnSuccess Always empty thus no items to work with.", + " M elementAt At most one item with index 0. Use [`defaultIfEmpty`](#defaultIfEmpty).", + " S elementAt Always one item with index 0.", + " C elementAt Always empty thus no items to work with.", + " M elementAtOrError At most one item with index 0. Use [`toSingle`](#toSingle).", + " S elementAtOrError Always one item with index 0.", + " C elementAtOrError Always empty thus no items to work with.", + " S empty Never empty.", + " C empty Use [`complete()`](#complete).", + " C filter Always empty thus no items to work with.", + " M first At most one item. Use [`defaultIfEmpty`](#defaultIfEmpty).", + " S first Always one item.", + " C first Always empty. Use [`andThen()`](#andThen) to chose the follow-up sequence.", + " M firstElement At most one item, would be no-op.", + " S firstElement Always one item, would be no-op.", + " C firstElement Always empty.", + " M firstOrError At most one item, would be no-op.", + " S firstOrError Always one item, would be no-op.", + " C firstOrError Always empty. Use [`andThen()`](#andThen) and [`error()`](#error).", + " MS firstOrErrorStage At most one item. Use [`toCompletionStage()`](#toCompletionStage).", + " C firstOrErrorStage Always empty. Use [`andThen()`](#andThen), [`error()`](#error) and [`toCompletionStage()`](#toCompletionStage).", + " MSC firstStage At most one item. Use [`toCompletionStage()`](#toCompletionStage).", + " C flatMap Always empty thus no items to map.", + " C flatMapCompletable Always empty thus no items to map.", + " C flatMapCompletableDelayError Always empty thus no items to map.", + " C flatMapIterable Always empty thus no items to map.", + " M flatMapMaybe Use [`flatMap()`](#flatMap).", + " C flatMapMaybe Always empty thus no items to map.", + " C flatMapMaybeDelayError Always empty thus no items to map.", + " S flatMapSingle Use [`flatMap()`](#flatMap).", + " C flatMapSingle Always empty thus no items to map.", + " C flatMapStream Always empty thus no items to map.", + " MS flatMapIterable At most one item. Use [`flattenAsFlowable`](#flattenAsFlowable) or [`flattenAsObservable`](#flattenAsObservable).", + " MS flatMapStream At most one item. Use [`flattenStreamAsFlowable`](#flattenStreamAsFlowable) or [`flattenStreamAsObservable`](#flattenStreamAsObservable).", + "F flatMapObservable Not supported. Use [`flatMap`](#flatMap) and [`toFlowable()`](#toFlowable).", + " O flatMapObservable Use [`flatMap`](#flatMap).", + " C flatMapObservable Always empty thus no items to map.", + " O flatMapPublisher Not supported. Use [`flatMap`](#flatMap) and [`toObservable()`](#toFlowable).", + "F flatMapPublisher Use [`flatMap`](#flatMap).", + " C flatMapPublisher Always empty thus no items to map.", + "FO flatMapSingleElement Use [`flatMapSingle`](#flatMapSingle).", + " S flatMapSingleElement Use [`flatMap`](#flatMap).", + " C flatMapSingleElement Always empty thus no items to map.", + "FO flattenAsFlowable Use [`flatMapIterable()`](#flatMapIterable).", + " C flattenAsFlowable Always empty thus no items to map.", + "FO flattenAsObservable Use [`flatMapIterable()`](#flatMapIterable).", + " C flattenAsObservable Always empty thus no items to map.", + "FO flattenStreamAsFlowable Use [`flatMapStream()`](#flatMapStream).", + " C flattenStreamAsFlowable Always empty thus no items to map.", + "FO flattenStreamAsObservable Use [`flatMapStream()`](#flatMapStream).", + " C flattenStreamAsObservable Always empty thus no items to map.", + " MSC forEach Use [`subscribe()`](#subscribe).", + " MSC forEachWhile Use [`subscribe()`](#subscribe).", + " S fromAction Never empty.", + " M fromArray At most one item. Use [`just()`](#just) or [`empty()`](#empty).", + " S fromArray Always one item. Use [`just()`](#just).", + " C fromArray Always empty. Use [`complete()`](#complete).", + " S fromCompletable Always error.", + " C fromCompletable Use [`wrap()`](#wrap).", + " M fromIterable At most one item. Use [`just()`](#just) or [`empty()`](#empty).", + " S fromIterable Always one item. Use [`just()`](#just).", + " C fromIterable Always empty. Use [`complete()`](#complete).", + " M fromMaybe Use [`wrap()`](#wrap).", + " O fromObservable Use [`wrap()`](#wrap).", + " S fromOptional Always one item. Use [`just()`](#just).", + " C fromOptional Always empty. Use [`complete()`](#complete).", + " S fromRunnable Never empty.", + " S fromSingle Use [`wrap()`](#wrap).", + " M fromStream At most one item. Use [`just()`](#just) or [`empty()`](#empty).", + " S fromStream Always one item. Use [`just()`](#just).", + " C fromStream Always empty. Use [`complete()`](#complete).", + " MSC generate Use [`fromSupplier()`](#fromSupplier).", + " MS groupBy At most one item.", + " C groupBy Always empty thus no items to group.", + " MS groupJoin At most one item.", + " C groupJoin Always empty thus no items to join.", + "FO ignoreElement Use [`ignoreElements()`](#ignoreElements).", + " C ignoreElement Always empty.", + " MS ignoreElements Use [`ignoreElement()`](#ignoreElement).", + " C ignoreElements Always empty.", + " MSC interval At most one item. Use [`timer()`](#timer).", + " MSC intervalRange At most one item. Use [`timer()`](#timer).", + " S isEmpty Always one item.", + " C isEmpty Always empty.", + " MS join At most one item. Use [`zip()`](#zip)", + " C join Always empty thus no items to join.", + " C just Always empty.", + " M last At most one item. Use [`defaultIfEmpty`](#defaultIfEmpty).", + " S last Always one item.", + " C last Always empty. Use [`andThen()`](#andThen) to chose the follow-up sequence.", + " M lastElement At most one item, would be no-op.", + " S lastElement Always one item, would be no-op.", + " C lastElement Always empty.", + " M lastOrError At most one item, would be no-op.", + " S lastOrError Always one item, would be no-op.", + " C lastOrError Always empty. Use [`andThen()`](#andThen) and [`error()`](#error).", + " MS lastOrErrorStage At most one item. Use [`toCompletionStage()`](#toCompletionStage).", + " C lastOrErrorStage Always empty. Use [`andThen()`](#andThen), [`error()`](#error) and [`toCompletionStage()`](#toCompletionStage).", + " MSC lastStage At most one item. Use [`toCompletionStage()`](#toCompletionStage).", + " C map Always empty thus no items to map.", + " C mapOptional Always empty thus no items to map.", + " C ofType Always empty thus no items to filter.", + " OMSC onBackpressureBuffer Backpressure related and not supported outside `Flowable`.", + " OMSC onBackpressureDrop Backpressure related and not supported outside `Flowable`.", + " OMSC onBackpressureLatest Backpressure related and not supported outside `Flowable`.", + " OMSC parallel Needs backpressure thus not supported outside `Flowable`.", + " M publish Connectable sources not supported outside `Flowable` and `Observable`. Use a `MaybeSubject`.", + " S publish Connectable sources not supported outside `Flowable` and `Observable`. Use a `SingleSubject`.", + " C publish Connectable sources not supported outside `Flowable` and `Observable`. Use a `ConnectableSubject`.", + " MS range At most one item. Use [`just()`](#just).", + " C range Always empty. Use [`complete()`](#complete).", + " MS rangeLong At most one item. Use [`just()`](#just).", + " C rangeLong Always empty. Use [`complete()`](#complete).", + " OMSC rebatchRequests Backpressure related and not supported outside `Flowable`.", + " MS reduce At most one item. Use [`map()`](#map).", + " C reduce Always empty thus no items to reduce.", + " MS reduceWith At most one item. Use [`map()`](#map).", + " C reduceWith Always empty thus no items to reduce.", + " M replay Connectable sources not supported outside `Flowable` and `Observable`. Use a `MaybeSubject`.", + " S replay Connectable sources not supported outside `Flowable` and `Observable`. Use a `SingleSubject`.", + " C replay Connectable sources not supported outside `Flowable` and `Observable`. Use a `ConnectableSubject`.", + " MS sample At most one item, would be no-op.", + " C sample Always empty thus no items to work with.", + " MS scan At most one item. Use [`map()`](#map).", + " C scan Always empty thus no items to reduce.", + " MS scanWith At most one item. Use [`map()`](#map).", + " C scanWith Always empty thus no items to reduce.", + " MSC serialize At most one signal type.", + " M share Connectable sources not supported outside `Flowable` and `Observable`. Use a `MaybeSubject`.", + " S share Connectable sources not supported outside `Flowable` and `Observable`. Use a `SingleSubject`.", + " C share Connectable sources not supported outside `Flowable` and `Observable`. Use a `ConnectableSubject`.", + " M single At most one item. Use [`defaultIfEmpty`](#defaultIfEmpty).", + " S single Always one item.", + " C single Always empty. Use [`andThen()`](#andThen) to chose the follow-up sequence.", + " M singleElement At most one item, would be no-op.", + " S singleElement Always one item, would be no-op.", + " C singleElement Always empty.", + " M singleOrError At most one item, would be no-op.", + " S singleOrError Always one item, would be no-op.", + " C singleOrError Always empty. Use [`andThen()`](#andThen) and [`error()`](#error).", + " MS singleOrErrorStage At most one item. Use [`toCompletionStage()`](#toCompletionStage).", + " C singleOrErrorStage Always empty. Use [`andThen()`](#andThen), [`error()`](#error) and [`toCompletionStage()`](#toCompletionStage).", + " MSC singleStage At most one item. Use [`toCompletionStage()`](#toCompletionStage).", + " MSC skip At most one item, would be no-op.", + " MSC skipLast At most one item, would be no-op.", + " MS skipWhile At most one item. Use [`filter()`](#filter).", + " C skipWhile Always empty.", + " MSC skipUntil At most one item. Use [`takeUntil()`](#takeUntil).", + " MSC sorted At most one item.", + " MSC startWithArray Use [`startWith()`](#startWith) and [`fromArray()`](#fromArray) of `Flowable` or `Observable`.", + " MSC startWithItem Use [`startWith()`](#startWith) and [`just()`](#just) of another reactive type.", + " MSC startWithIterable Use [`startWith()`](#startWith) and [`fromIterable()`](#fromArray) of `Flowable` or `Observable`.", + " S switchIfEmpty Never empty.", + " C switchIfEmpty Always empty. Use [`defaultIfEmpty()`](#defaultIfEmpty).", + " MS switchMap At most one item. Use [`flatMap()`](#flatMap).", + " C switchMap Always empty thus no items to map.", + " MS switchMapDelayError At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapDelayError Always empty thus no items to map.", + " MS switchMapCompletable At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapCompletable Always empty thus no items to map.", + " MS switchMapCompletableDelayError At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapCompletableDelayError Always empty thus no items to map.", + " MS switchMapMaybe At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapMaybe Always empty thus no items to map.", + " MS switchMapMaybeDelayError At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapMaybeDelayError Always empty thus no items to map.", + " MS switchMapSingle At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapSingle Always empty thus no items to map.", + " MS switchMapSingleDelayError At most one item. Use [`flatMap()`](#flatMap).", + " C switchMapSingleDelayError Always empty thus no items to map.", + " MSC take At most one item, would be no-op.", + " MSC takeLast At most one item, would be no-op.", + " MS takeWhile At most one item. Use [`filter()`](#filter).", + " C takeWhile Always empty.", + " MS throttleFirst At most one item signaled so no subsequent items to work with.", + " C throttleFirst Always empty thus no items to work with.", + " MS throttleLast At most one item signaled so no subsequent items to work with.", + " C throttleLast Always empty thus no items to work with.", + " MS throttleLatest At most one item signaled so no subsequent items to work with.", + " C throttleLatest Always empty thus no items to work with.", + " MS throttleWithTimeout At most one item signaled so no subsequent items to work with.", + " C throttleWithTimeout Always empty thus no items to work with.", + " C timeInterval Always empty thus no items to work with.", + " C timestamp Always empty thus no items to work with.", + "FO toCompletionStage Use [`firstStage`](#firstStage), [`lastStage`](#lastStage) or [`singleStage`](#singleStage).", + "F toFlowable Would be no-op.", + " M toList At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S toList One element to collect. Use [`map()`](#map) to transform into a list/collection.", + " C toList Always empty. Use [`andThen()`](#andThen) to bring in a collection.", + " M toMap At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S toMap One element to collect. Use [`map()`](#map) to transform into a list/collection.", + " C toMap Always empty. Use [`andThen()`](#andThen) to bring in a collection.", + " M toMultimap At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S toMultimap One element to collect. Use [`map()`](#map) to transform into a list/collection.", + " C toMultimap Always empty. Use [`andThen()`](#andThen) to bring in a collection.", + "FO toMaybe Use [`firstElement`](#firstElement), [`lastElement`](#lastElement) or [`singleElement`](#singleElement).", + " M toMaybe Would be no-op.", + " O toObservable Would be no-op.", + "FO toSingle Use [`firstOrError`](#firstOrError), [`lastOrError`](#lastOrError) or [`singleOrError`](#singleOrError).", + " S toSingle Would be no-op.", + "FO toSingleDefault Use [`first`](#first), [`last`](#last) or [`single`](#single).", + " M toSingleDefault Use [`defaultIfEmpty()`](#defaultIfEmpty).", + " S toSingleDefault Would be no-op.", + " M toSortedList At most one element to collect. Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a list/collection.", + " S toSortedList One element to collect. Use [`map()`](#map) to transform into a list/collection.", + " C toSortedList Always empty. Use [`andThen()`](#andThen) to bring in a collection.", + " M window Use [`map()`](#map) and [`switchIfEmpty()`](#switchIfEmpty) to transform into a nested source.", + " S window Use [`map()`](#map) to transform into a nested source.", + " C window Always empty. Use [`andThen()`](#andThen) to bring in a nested source.", + " MS withLatestFrom At most one element per source. Use [`zip()`](#zip).", + " C withLatestFrom Always empty. Use [`merge()`](#merge).", + "F wrap Use [`fromPublisher()`](#fromPublisher).", + " C zip Use [`merge()`](#merge).", + " C zipArray Use [`mergeArray()`](#mergeArray).", + " C zipWith Use [`mergeWith()`](#mergeWith).", + }; + + static final Map> NOTES_MAP; + static { + NOTES_MAP = new HashMap<>(); + for (String s : NOTES) { + char[] classes = s.substring(0, 5).trim().toCharArray(); + int idx = s.indexOf(' ', 7); + String method = s.substring(6, idx); + String note = s.substring(idx).trim(); + + for (char c : classes) { + NOTES_MAP.computeIfAbsent(method, v -> new HashMap<>()) + .put(String.valueOf(c), note); + } + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/QueueDrainHelperTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/QueueDrainHelperTest.java index 9060e85c45..7929ea8782 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/QueueDrainHelperTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/QueueDrainHelperTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,9 +27,9 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.BooleanSupplier; -import io.reactivex.rxjava3.internal.queue.SpscArrayQueue; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.SpscArrayQueue; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -89,8 +89,8 @@ public void cancel() { @Test public void postCompleteEmpty() { - TestSubscriber ts = new TestSubscriber(); - ArrayDeque queue = new ArrayDeque(); + TestSubscriber ts = new TestSubscriber<>(); + ArrayDeque queue = new ArrayDeque<>(); AtomicLong state = new AtomicLong(); BooleanSupplier isCancelled = new BooleanSupplier() { @Override @@ -108,8 +108,8 @@ public boolean getAsBoolean() throws Exception { @Test public void postCompleteWithRequest() { - TestSubscriber ts = new TestSubscriber(); - ArrayDeque queue = new ArrayDeque(); + TestSubscriber ts = new TestSubscriber<>(); + ArrayDeque queue = new ArrayDeque<>(); AtomicLong state = new AtomicLong(); BooleanSupplier isCancelled = new BooleanSupplier() { @Override @@ -130,8 +130,8 @@ public boolean getAsBoolean() throws Exception { @Test public void completeRequestRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); - final ArrayDeque queue = new ArrayDeque(); + final TestSubscriber ts = new TestSubscriber<>(); + final ArrayDeque queue = new ArrayDeque<>(); final AtomicLong state = new AtomicLong(); final BooleanSupplier isCancelled = new BooleanSupplier() { @Override @@ -165,8 +165,8 @@ public void run() { @Test public void postCompleteCancelled() { - final TestSubscriber ts = new TestSubscriber(); - ArrayDeque queue = new ArrayDeque(); + final TestSubscriber ts = new TestSubscriber<>(); + ArrayDeque queue = new ArrayDeque<>(); AtomicLong state = new AtomicLong(); BooleanSupplier isCancelled = new BooleanSupplier() { @Override @@ -194,7 +194,7 @@ public void onNext(Integer t) { cancel(); } }; - ArrayDeque queue = new ArrayDeque(); + ArrayDeque queue = new ArrayDeque<>(); AtomicLong state = new AtomicLong(); BooleanSupplier isCancelled = new BooleanSupplier() { @Override @@ -214,7 +214,7 @@ public boolean getAsBoolean() throws Exception { @Test public void drainMaxLoopMissingBackpressure() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -259,7 +259,7 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); q.offer(1); QueueDrainHelper.drainMaxLoop(q, ts, false, null, qd); @@ -269,7 +269,7 @@ public boolean accept(Subscriber a, Integer v) { @Test public void drainMaxLoopMissingBackpressureWithResource() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -314,10 +314,10 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); q.offer(1); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); QueueDrainHelper.drainMaxLoop(q, ts, false, d, qd); @@ -328,7 +328,7 @@ public boolean accept(Subscriber a, Integer v) { @Test public void drainMaxLoopDontAccept() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -373,7 +373,7 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); q.offer(1); QueueDrainHelper.drainMaxLoop(q, ts, false, null, qd); @@ -383,7 +383,7 @@ public boolean accept(Subscriber a, Integer v) { @Test public void checkTerminatedDelayErrorEmpty() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -428,7 +428,7 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, true, ts, true, q, qd); @@ -437,7 +437,7 @@ public boolean accept(Subscriber a, Integer v) { @Test public void checkTerminatedDelayErrorNonEmpty() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -482,7 +482,7 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, false, ts, true, q, qd); @@ -491,7 +491,7 @@ public boolean accept(Subscriber a, Integer v) { @Test public void checkTerminatedDelayErrorEmptyError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -536,7 +536,7 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, true, ts, true, q, qd); @@ -545,7 +545,7 @@ public boolean accept(Subscriber a, Integer v) { @Test public void checkTerminatedNonDelayErrorError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); QueueDrain qd = new QueueDrain() { @@ -590,7 +590,7 @@ public boolean accept(Subscriber a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, false, ts, false, q, qd); @@ -599,8 +599,8 @@ public boolean accept(Subscriber a, Integer v) { @Test public void observerCheckTerminatedDelayErrorEmpty() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); ObservableQueueDrain qd = new ObservableQueueDrain() { @Override @@ -633,7 +633,7 @@ public void accept(Observer a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, true, to, true, q, null, qd); @@ -642,8 +642,8 @@ public void accept(Observer a, Integer v) { @Test public void observerCheckTerminatedDelayErrorEmptyResource() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); ObservableQueueDrain qd = new ObservableQueueDrain() { @Override @@ -676,9 +676,9 @@ public void accept(Observer a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); QueueDrainHelper.checkTerminated(true, true, to, true, q, d, qd); @@ -689,8 +689,8 @@ public void accept(Observer a, Integer v) { @Test public void observerCheckTerminatedDelayErrorNonEmpty() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); ObservableQueueDrain qd = new ObservableQueueDrain() { @Override @@ -723,7 +723,7 @@ public void accept(Observer a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, false, to, true, q, null, qd); @@ -732,8 +732,8 @@ public void accept(Observer a, Integer v) { @Test public void observerCheckTerminatedDelayErrorEmptyError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); ObservableQueueDrain qd = new ObservableQueueDrain() { @Override @@ -766,7 +766,7 @@ public void accept(Observer a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, true, to, true, q, null, qd); @@ -775,8 +775,8 @@ public void accept(Observer a, Integer v) { @Test public void observerCheckTerminatedNonDelayErrorError() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); ObservableQueueDrain qd = new ObservableQueueDrain() { @Override @@ -809,7 +809,7 @@ public void accept(Observer a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); QueueDrainHelper.checkTerminated(true, false, to, false, q, null, qd); @@ -818,8 +818,8 @@ public void accept(Observer a, Integer v) { @Test public void observerCheckTerminatedNonDelayErrorErrorResource() { - TestObserver to = new TestObserver(); - to.onSubscribe(Disposables.empty()); + TestObserver to = new TestObserver<>(); + to.onSubscribe(Disposable.empty()); ObservableQueueDrain qd = new ObservableQueueDrain() { @Override @@ -852,9 +852,9 @@ public void accept(Observer a, Integer v) { } }; - SpscArrayQueue q = new SpscArrayQueue(32); + SpscArrayQueue q = new SpscArrayQueue<>(32); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); QueueDrainHelper.checkTerminated(true, false, to, false, q, d, qd); @@ -866,9 +866,9 @@ public void accept(Observer a, Integer v) { @Test public void postCompleteAlreadyComplete() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - Queue q = new ArrayDeque(); + Queue q = new ArrayDeque<>(); q.offer(1); AtomicLong state = new AtomicLong(QueueDrainHelper.COMPLETED_MASK); diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/TestingHelper.java b/src/test/java/io/reactivex/rxjava3/internal/util/TestingHelper.java index 867f518d60..207c85236d 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/TestingHelper.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/TestingHelper.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.internal.util; import java.util.*; @@ -40,7 +38,7 @@ public static Supplier> supplierListCreator() { @Override public List get() { - return new ArrayList(); + return new ArrayList<>(); } }; } diff --git a/src/test/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayListTest.java b/src/test/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayListTest.java index e087542ff3..3ce08aa3dc 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayListTest.java +++ b/src/test/java/io/reactivex/rxjava3/internal/util/VolatileSizeArrayListTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,14 +25,14 @@ public class VolatileSizeArrayListTest extends RxJavaTest { @Test public void normal() { - List list = new VolatileSizeArrayList(); + List list = new VolatileSizeArrayList<>(); assertTrue(list.isEmpty()); assertEquals(0, list.size()); assertFalse(list.contains(1)); assertFalse(list.remove((Integer)1)); - list = new VolatileSizeArrayList(16); + list = new VolatileSizeArrayList<>(16); assertTrue(list.add(1)); assertTrue(list.addAll(Arrays.asList(3, 4, 7))); list.add(1, 2); @@ -81,7 +81,7 @@ public void normal() { assertEquals(Arrays.asList(3, 4, 5), list.subList(2, 5)); - VolatileSizeArrayList list2 = new VolatileSizeArrayList(); + VolatileSizeArrayList list2 = new VolatileSizeArrayList<>(); list2.addAll(Arrays.asList(1, 2, 3, 4, 5, 6)); assertNotEquals(list2, list); @@ -91,7 +91,7 @@ public void normal() { assertEquals(list2, list); assertEquals(list, list2); - List list3 = new ArrayList(); + List list3 = new ArrayList<>(); list3.addAll(Arrays.asList(1, 2, 3, 4, 5, 6)); assertNotEquals(list3, list); diff --git a/src/test/java/io/reactivex/rxjava3/maybe/MaybeCreateTest.java b/src/test/java/io/reactivex/rxjava3/maybe/MaybeCreateTest.java index d681c85c0c..e3bbfb4bfc 100644 --- a/src/test/java/io/reactivex/rxjava3/maybe/MaybeCreateTest.java +++ b/src/test/java/io/reactivex/rxjava3/maybe/MaybeCreateTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,16 +27,11 @@ import io.reactivex.rxjava3.testsupport.TestHelper; public class MaybeCreateTest extends RxJavaTest { - @Test(expected = NullPointerException.class) - public void nullArgument() { - Maybe.create(null); - } - @Test public void basic() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override @@ -61,8 +56,8 @@ public void subscribe(MaybeEmitter e) throws Exception { public void basicWithCancellable() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d1 = Disposables.empty(); - final Disposable d2 = Disposables.empty(); + final Disposable d1 = Disposable.empty(); + final Disposable d2 = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override @@ -95,7 +90,7 @@ public void cancel() throws Exception { public void basicWithError() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override @@ -120,7 +115,7 @@ public void subscribe(MaybeEmitter e) throws Exception { public void basicWithCompletion() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/maybe/MaybeRetryTest.java b/src/test/java/io/reactivex/rxjava3/maybe/MaybeRetryTest.java index f260a78a82..731dd03e23 100644 --- a/src/test/java/io/reactivex/rxjava3/maybe/MaybeRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/maybe/MaybeRetryTest.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-present, RxJava Contributors. +/* + * 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 @@ -21,6 +21,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Predicate; import io.reactivex.rxjava3.internal.functions.Functions; @@ -120,4 +121,58 @@ public void retryTimesPredicateWithZeroRetries() { assertEquals(1, numberOfSubscribeCalls.get()); } + + @Test + public void untilTrueJust() { + Maybe.just(1) + .retryUntil(() -> true) + .test() + .assertResult(1); + } + + @Test + public void untilFalseJust() { + Maybe.just(1) + .retryUntil(() -> false) + .test() + .assertResult(1); + } + + @Test + public void untilTrueEmpty() { + Maybe.empty() + .retryUntil(() -> true) + .test() + .assertResult(); + } + + @Test + public void untilFalseEmpty() { + Maybe.empty() + .retryUntil(() -> false) + .test() + .assertResult(); + } + + @Test + public void untilTrueError() { + Maybe.error(new TestException()) + .retryUntil(() -> true) + .test() + .assertFailure(TestException.class); + } + + @Test + public void untilFalseError() { + AtomicInteger counter = new AtomicInteger(); + Maybe.defer(() -> { + if (counter.getAndIncrement() == 0) { + return Maybe.error(new TestException()); + } + return Maybe.just(1); + }) + .retryUntil(() -> false) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/maybe/MaybeTest.java b/src/test/java/io/reactivex/rxjava3/maybe/MaybeTest.java index 3796f365c0..acc7066c29 100644 --- a/src/test/java/io/reactivex/rxjava3/maybe/MaybeTest.java +++ b/src/test/java/io/reactivex/rxjava3/maybe/MaybeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,10 +30,10 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.operators.flowable.FlowableZipTest.ArgsToString; import io.reactivex.rxjava3.internal.operators.maybe.*; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -172,11 +172,6 @@ public void just() { .assertResult(1); } - @Test(expected = NullPointerException.class) - public void justNull() { - Maybe.just(null); - } - @Test public void empty() { Maybe.empty() @@ -194,16 +189,6 @@ public void never() { .assertNotComplete(); } - @Test(expected = NullPointerException.class) - public void errorNull() { - Maybe.error((Throwable)null); - } - - @Test(expected = NullPointerException.class) - public void errorSupplierNull() { - Maybe.error((Supplier)null); - } - @Test public void error() { Maybe.error(new TestException()) @@ -230,7 +215,7 @@ public void wrapCustom() { Maybe.wrap(new MaybeSource() { @Override public void subscribe(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(1); } }) @@ -243,11 +228,6 @@ public void wrapMaybe() { assertSame(Maybe.empty(), Maybe.wrap(Maybe.empty())); } - @Test(expected = NullPointerException.class) - public void wrapNull() { - Maybe.wrap(null); - } - @Test public void emptySingleton() { assertSame(Maybe.empty(), Maybe.empty()); @@ -258,11 +238,6 @@ public void neverSingleton() { assertSame(Maybe.never(), Maybe.never()); } - @Test(expected = NullPointerException.class) - public void liftNull() { - Maybe.just(1).lift(null); - } - @Test public void liftJust() { Maybe.just(1).lift(new MaybeOperator() { @@ -287,11 +262,6 @@ public MaybeObserver apply(MaybeObserver t) th .assertFailure(TestException.class); } - @Test(expected = NullPointerException.class) - public void deferNull() { - Maybe.defer(null); - } - @Test public void deferThrows() { Maybe.defer(new Supplier>() { @@ -356,7 +326,7 @@ public void unsafeCreate() { Maybe.unsafeCreate(new MaybeSource() { @Override public void subscribe(MaybeObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess(1); } }) @@ -364,11 +334,6 @@ public void subscribe(MaybeObserver observer) { .assertResult(1); } - @Test(expected = NullPointerException.class) - public void unsafeCreateNull() { - Maybe.unsafeCreate(null); - } - @Test public void to() { Maybe.just(1).to(new MaybeConverter>() { @@ -393,16 +358,6 @@ public Flowable apply(Maybe v) { .assertResult(1); } - @Test(expected = NullPointerException.class) - public void toNull() { - Maybe.just(1).to(null); - } - - @Test(expected = NullPointerException.class) - public void asNull() { - Maybe.just(1).to(null); - } - @Test public void compose() { Maybe.just(1).compose(new MaybeTransformer() { @@ -420,16 +375,6 @@ public Integer apply(Integer w) throws Exception { .assertResult(2); } - @Test(expected = NullPointerException.class) - public void composeNull() { - Maybe.just(1).compose(null); - } - - @Test(expected = NullPointerException.class) - public void mapNull() { - Maybe.just(1).map(null); - } - @Test public void mapReturnNull() { Maybe.just(1).map(new Function() { @@ -460,11 +405,6 @@ public String apply(Integer v) throws Exception { }).test().assertResult("1"); } - @Test(expected = NullPointerException.class) - public void filterNull() { - Maybe.just(1).filter(null); - } - @Test public void filterThrows() { Maybe.just(1).filter(new Predicate() { @@ -505,11 +445,6 @@ public boolean test(Integer v) throws Exception { }).test().assertResult(); } - @Test(expected = NullPointerException.class) - public void singleFilterNull() { - Single.just(1).filter(null); - } - @Test public void singleFilterThrows() { Single.just(1).filter(new Predicate() { @@ -547,16 +482,6 @@ public void cast() { to.assertResult((Number)1); } - @Test(expected = NullPointerException.class) - public void observeOnNull() { - Maybe.just(1).observeOn(null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnNull() { - Maybe.just(1).observeOn(null); - } - @Test public void observeOnSuccess() { String main = Thread.currentThread().getName(); @@ -896,7 +821,7 @@ public void run() throws Exception { @Test public void observeOnDispose() throws Exception { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final CountDownLatch cdl = new CountDownLatch(1); @@ -1382,13 +1307,11 @@ public void concat3Backpressured() { ts.assertResult(1, 2, 3); } - @SuppressWarnings("unchecked") @Test public void concatArrayZero() { assertSame(Flowable.empty(), Maybe.concatArray()); } - @SuppressWarnings("unchecked") @Test public void concatArrayOne() { Maybe.concatArray(Maybe.just(1)).test().assertResult(1); @@ -1401,7 +1324,6 @@ public void concat4() { .assertResult(1, 2, 3, 4); } - @SuppressWarnings("unchecked") @Test public void concatIterable() { Maybe.concat(Arrays.asList(Maybe.just(1), Maybe.just(2))) @@ -1409,7 +1331,6 @@ public void concatIterable() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void concatIterableEmpty() { Maybe.concat(Arrays.asList(Maybe.empty(), Maybe.empty())) @@ -1417,7 +1338,6 @@ public void concatIterableEmpty() { .assertResult(); } - @SuppressWarnings("unchecked") @Test public void concatIterableBackpressured() { TestSubscriber ts = Maybe.concat(Arrays.asList(Maybe.just(1), Maybe.just(2))) @@ -1434,7 +1354,6 @@ public void concatIterableBackpressured() { ts.assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void concatIterableBackpressuredNonEager() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1494,16 +1413,11 @@ public void concatPublisherPrefetch() { Maybe.concat(Flowable.just(Maybe.just(1), Maybe.just(2)), 1).test().assertResult(1, 2); } - @Test(expected = NullPointerException.class) - public void nullArgument() { - Maybe.create(null); - } - @Test public void basic() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override @@ -1533,7 +1447,7 @@ public void subscribe(MaybeEmitter e) throws Exception { public void basicWithError() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override @@ -1561,7 +1475,7 @@ public void subscribe(MaybeEmitter e) throws Exception { public void basicWithComplete() { List errors = TestHelper.trackPluginErrors(); try { - final Disposable d = Disposables.empty(); + final Disposable d = Disposable.empty(); Maybe.create(new MaybeOnSubscribe() { @Override @@ -1598,7 +1512,6 @@ public void maybeToPublisherEnum() { TestHelper.checkEnum(MaybeToPublisher.class); } - @SuppressWarnings("unchecked") @Test public void ambArrayOneIsNull() { Maybe.ambArray(null, Maybe.just(1)) @@ -1606,13 +1519,11 @@ public void ambArrayOneIsNull() { .assertError(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void ambArrayEmpty() { assertSame(Maybe.empty(), Maybe.ambArray()); } - @SuppressWarnings("unchecked") @Test public void ambArrayOne() { assertSame(Maybe.never(), Maybe.ambArray(Maybe.never())); @@ -1624,21 +1535,18 @@ public void ambWithOrder() { Maybe.just(1).ambWith(error).test().assertValue(1); } - @SuppressWarnings("unchecked") @Test public void ambIterableOrder() { Maybe error = Maybe.error(new RuntimeException()); Maybe.amb(Arrays.asList(Maybe.just(1), error)).test().assertValue(1); } - @SuppressWarnings("unchecked") @Test public void ambArrayOrder() { Maybe error = Maybe.error(new RuntimeException()); Maybe.ambArray(Maybe.just(1), error).test().assertValue(1); } - @SuppressWarnings("unchecked") @Test public void ambArray1SignalsSuccess() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1661,7 +1569,6 @@ public void ambArray1SignalsSuccess() { to.assertResult(1); } - @SuppressWarnings("unchecked") @Test public void ambArray2SignalsSuccess() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1684,7 +1591,6 @@ public void ambArray2SignalsSuccess() { to.assertResult(2); } - @SuppressWarnings("unchecked") @Test public void ambArray1SignalsError() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1706,7 +1612,6 @@ public void ambArray1SignalsError() { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void ambArray2SignalsError() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1728,7 +1633,6 @@ public void ambArray2SignalsError() { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void ambArray1SignalsComplete() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1750,7 +1654,6 @@ public void ambArray1SignalsComplete() { to.assertResult(); } - @SuppressWarnings("unchecked") @Test public void ambArray2SignalsComplete() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1772,7 +1675,6 @@ public void ambArray2SignalsComplete() { to.assertResult(); } - @SuppressWarnings("unchecked") @Test public void ambIterable1SignalsSuccess() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1795,7 +1697,6 @@ public void ambIterable1SignalsSuccess() { to.assertResult(1); } - @SuppressWarnings("unchecked") @Test public void ambIterable2SignalsSuccess() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1818,7 +1719,6 @@ public void ambIterable2SignalsSuccess() { to.assertResult(2); } - @SuppressWarnings("unchecked") @Test public void ambIterable2SignalsSuccessWithOverlap() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1842,7 +1742,6 @@ public void ambIterable2SignalsSuccessWithOverlap() { to.assertResult(2); } - @SuppressWarnings("unchecked") @Test public void ambIterable1SignalsError() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1864,7 +1763,6 @@ public void ambIterable1SignalsError() { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void ambIterable2SignalsError() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1886,7 +1784,6 @@ public void ambIterable2SignalsError() { to.assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void ambIterable2SignalsErrorWithOverlap() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1909,7 +1806,6 @@ public void ambIterable2SignalsErrorWithOverlap() { to.assertFailureAndMessage(TestException.class, "2"); } - @SuppressWarnings("unchecked") @Test public void ambIterable1SignalsComplete() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1931,7 +1827,6 @@ public void ambIterable1SignalsComplete() { to.assertResult(); } - @SuppressWarnings("unchecked") @Test public void ambIterable2SignalsComplete() { PublishProcessor pp1 = PublishProcessor.create(); @@ -1953,11 +1848,6 @@ public void ambIterable2SignalsComplete() { to.assertResult(); } - @Test(expected = NullPointerException.class) - public void ambIterableNull() { - Maybe.amb((Iterable>)null); - } - @Test public void ambIterableIteratorNull() { Maybe.amb(new Iterable>() { @@ -1968,7 +1858,6 @@ public Iterator> iterator() { }).test().assertError(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void ambIterableOneIsNull() { Maybe.amb(Arrays.asList(null, Maybe.just(1))) @@ -1986,7 +1875,6 @@ public void ambIterableOne() { Maybe.amb(Collections.singleton(Maybe.just(1))).test().assertResult(1); } - @SuppressWarnings("unchecked") @Test public void mergeArray() { Maybe.mergeArray(Maybe.just(1), Maybe.just(2), Maybe.just(3)) @@ -2023,7 +1911,6 @@ public void merge4Take2() { .assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeArrayBackpressured() { TestSubscriber ts = Maybe.mergeArray(Maybe.just(1), Maybe.just(2), Maybe.just(3)) @@ -2043,7 +1930,6 @@ public void mergeArrayBackpressured() { ts.assertResult(1, 2, 3); } - @SuppressWarnings("unchecked") @Test public void mergeArrayBackpressuredMixed1() { TestSubscriber ts = Maybe.mergeArray(Maybe.just(1), Maybe.empty(), Maybe.just(3)) @@ -2060,7 +1946,6 @@ public void mergeArrayBackpressuredMixed1() { ts.assertResult(1, 3); } - @SuppressWarnings("unchecked") @Test public void mergeArrayBackpressuredMixed2() { TestSubscriber ts = Maybe.mergeArray(Maybe.just(1), Maybe.just(2), Maybe.empty()) @@ -2077,7 +1962,6 @@ public void mergeArrayBackpressuredMixed2() { ts.assertResult(1, 2); } - @SuppressWarnings("unchecked") @Test public void mergeArrayBackpressuredMixed3() { TestSubscriber ts = Maybe.mergeArray(Maybe.empty(), Maybe.just(2), Maybe.just(3)) @@ -2094,7 +1978,6 @@ public void mergeArrayBackpressuredMixed3() { ts.assertResult(2, 3); } - @SuppressWarnings("unchecked") @Test public void mergeArrayFused() { TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); @@ -2107,7 +1990,6 @@ public void mergeArrayFused() { .assertResult(1, 2, 3); } - @SuppressWarnings("unchecked") @Test public void mergeArrayFusedRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -2143,13 +2025,11 @@ public void run() { } } - @SuppressWarnings("unchecked") @Test public void mergeArrayZero() { assertSame(Flowable.empty(), Maybe.mergeArray()); } - @SuppressWarnings("unchecked") @Test public void mergeArrayOne() { Maybe.mergeArray(Maybe.just(1)).test().assertResult(1); @@ -2188,7 +2068,6 @@ public void mergeMaybe() { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void mergeIterable() { Maybe.merge(Arrays.asList(Maybe.just(1), Maybe.just(2), Maybe.just(3))) @@ -2282,7 +2161,7 @@ public void subscribeZeroError() { @Test public void subscribeToOnSuccess() { - final List values = new ArrayList(); + final List values = new ArrayList<>(); Consumer onSuccess = new Consumer() { @Override @@ -2302,7 +2181,7 @@ public void accept(Integer e) throws Exception { @Test public void subscribeToOnError() { - final List values = new ArrayList(); + final List values = new ArrayList<>(); Consumer onError = new Consumer() { @Override @@ -2323,7 +2202,7 @@ public void accept(Throwable e) throws Exception { @Test public void subscribeToOnComplete() { - final List values = new ArrayList(); + final List values = new ArrayList<>(); Action onComplete = new Action() { @Override @@ -2370,7 +2249,7 @@ public void onComplete() { @Test public void doOnEventSuccess() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); assertTrue(Maybe.just(1) .doOnEvent(new BiConsumer() { @@ -2389,7 +2268,7 @@ public void accept(Integer v, Throwable e) throws Exception { public void doOnEventError() { List errors = TestHelper.trackPluginErrors(); try { - final List list = new ArrayList(); + final List list = new ArrayList<>(); TestException ex = new TestException(); @@ -2413,7 +2292,7 @@ public void accept(Integer v, Throwable e) throws Exception { @Test public void doOnEventComplete() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); assertTrue(Maybe.empty() .doOnEvent(new BiConsumer() { @@ -2428,11 +2307,6 @@ public void accept(Integer v, Throwable e) throws Exception { assertEquals(Arrays.asList(null, null), list); } - @Test(expected = NullPointerException.class) - public void doOnEventNull() { - Maybe.just(1).doOnEvent(null); - } - @Test public void doOnEventSuccessThrows() { Maybe.just(1) @@ -2477,7 +2351,6 @@ public void accept(Integer v, Throwable e) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void concatArrayDelayError() { Maybe.concatArrayDelayError(Maybe.empty(), Maybe.just(1), Maybe.error(new TestException())) @@ -2493,7 +2366,6 @@ public void concatArrayDelayError() { assertFalse(Maybe.concatArrayDelayError(Maybe.never()) instanceof MaybeConcatArrayDelayError); } - @SuppressWarnings("unchecked") @Test public void concatIterableDelayError() { Maybe.concatDelayError(Arrays.asList(Maybe.empty(), Maybe.just(1), Maybe.error(new TestException()))) @@ -2516,7 +2388,17 @@ public void concatPublisherDelayError() { .assertFailure(TestException.class, 1); } - @SuppressWarnings("unchecked") + @Test + public void concatPublisherDelayErrorPrefetch() { + Maybe.concatDelayError(Flowable.just(Maybe.empty(), Maybe.just(1), Maybe.error(new TestException())), 1) + .test() + .assertFailure(TestException.class, 1); + + Maybe.concatDelayError(Flowable.just(Maybe.error(new TestException()), Maybe.empty(), Maybe.just(1)), 1) + .test() + .assertFailure(TestException.class, 1); + } + @Test public void concatEagerArray() { PublishProcessor pp1 = PublishProcessor.create(); @@ -2539,7 +2421,6 @@ public void concatEagerArray() { } - @SuppressWarnings("unchecked") @Test public void concatEagerIterable() { PublishProcessor pp1 = PublishProcessor.create(); @@ -2624,7 +2505,6 @@ public void fromFuture() { ; } - @SuppressWarnings("unchecked") @Test public void mergeArrayDelayError() { Maybe.mergeArrayDelayError(Maybe.empty(), Maybe.just(1), Maybe.error(new TestException())) @@ -2634,11 +2514,8 @@ public void mergeArrayDelayError() { Maybe.mergeArrayDelayError(Maybe.error(new TestException()), Maybe.empty(), Maybe.just(1)) .test() .assertFailure(TestException.class, 1); - - assertSame(Flowable.empty(), Maybe.mergeArrayDelayError()); } - @SuppressWarnings("unchecked") @Test public void mergeIterableDelayError() { Maybe.mergeDelayError(Arrays.asList(Maybe.empty(), Maybe.just(1), Maybe.error(new TestException()))) @@ -2704,7 +2581,7 @@ public void mergeDelayError4() { @Test public void sequenceEqual() { - Maybe.sequenceEqual(Maybe.just(1), Maybe.just(new Integer(1))).test().assertResult(true); + Maybe.sequenceEqual(Maybe.just(1_000_000), Maybe.just(Integer.valueOf(1_000_000))).test().assertResult(true); Maybe.sequenceEqual(Maybe.just(1), Maybe.just(2)).test().assertResult(false); @@ -2900,7 +2777,6 @@ public void zipArray() { .assertResult("[1]"); } - @SuppressWarnings("unchecked") @Test public void zipIterable() { Maybe.zip( @@ -3048,7 +2924,6 @@ public void ambWith2SignalsSuccess() { @Test public void zipIterableObject() { - @SuppressWarnings("unchecked") final List> maybes = Arrays.asList(Maybe.just(1), Maybe.just(4)); Maybe.zip(maybes, new Function() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/maybe/MaybeTimerTest.java b/src/test/java/io/reactivex/rxjava3/maybe/MaybeTimerTest.java index 03f229483b..286639042d 100644 --- a/src/test/java/io/reactivex/rxjava3/maybe/MaybeTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/maybe/MaybeTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableCombineLatestTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableCombineLatestTests.java index b4b11420d1..957268ecf1 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableCombineLatestTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableCombineLatestTests.java @@ -1,18 +1,16 @@ -/** +/* * 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. */ + package io.reactivex.rxjava3.observable; import org.junit.Test; diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableConcatTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableConcatTests.java index a9d1afca12..53c4f99029 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableConcatTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableConcatTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.observable; import static org.junit.Assert.assertEquals; @@ -62,7 +63,6 @@ public void concatWithIterableOfObservable() { Observable o2 = Observable.just("three", "four"); Observable o3 = Observable.just("five", "six"); - @SuppressWarnings("unchecked") Iterable> is = Arrays.asList(o1, o2, o3); List values = Observable.concat(Observable.fromIterable(is)).toList().blockingGet(); diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableCovarianceTest.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableCovarianceTest.java index 904af1d8a4..54cf0f9908 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableCovarianceTest.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableCovarianceTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.observable; @@ -66,7 +63,7 @@ public int compare(Media t1, Media t2) { @Test public void groupByCompose() { Observable movies = Observable.just(new HorrorMovie(), new ActionMovie(), new Movie()); - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); movies .groupBy(new Function() { @Override @@ -187,9 +184,9 @@ public Observable apply(List> listOfLists) { } else { // diff the two List newList = listOfLists.get(1); - List oldList = new ArrayList(listOfLists.get(0)); + List oldList = new ArrayList<>(listOfLists.get(0)); - Set delta = new LinkedHashSet(); + Set delta = new LinkedHashSet<>(); delta.addAll(newList); // remove all that match in old delta.removeAll(oldList); @@ -211,7 +208,7 @@ public Observable apply(List> listOfLists) { @Override public Observable apply(Observable> movieList) { return movieList - .startWithItem(new ArrayList()) + .startWithItem(new ArrayList<>()) .buffer(2, 1) .skip(1) .flatMap(calculateDelta); diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableDoOnTest.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableDoOnTest.java index 1e28d19b24..0e7ed9ef88 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableDoOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableDoOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class ObservableDoOnTest extends RxJavaTest { @Test public void doOnEach() { - final AtomicReference r = new AtomicReference(); + final AtomicReference r = new AtomicReference<>(); String output = Observable.just("one").doOnNext(new Consumer() { @Override public void accept(String v) { @@ -41,7 +41,7 @@ public void accept(String v) { @Test public void doOnError() { - final AtomicReference r = new AtomicReference(); + final AtomicReference r = new AtomicReference<>(); Throwable t = null; try { Observable. error(new RuntimeException("an error")) diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableErrorHandlingTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableErrorHandlingTests.java index bbbc80daed..9ec13895b0 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableErrorHandlingTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableErrorHandlingTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public class ObservableErrorHandlingTests extends RxJavaTest { @Test public void onNextError() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference caughtError = new AtomicReference(); + final AtomicReference caughtError = new AtomicReference<>(); Observable o = Observable.interval(50, TimeUnit.MILLISECONDS); Observer observer = new DefaultObserver() { @@ -69,7 +69,7 @@ public void onNext(Long args) { @Test public void onNextErrorAcrossThread() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference caughtError = new AtomicReference(); + final AtomicReference caughtError = new AtomicReference<>(); Observable o = Observable.interval(50, TimeUnit.MILLISECONDS); Observer observer = new DefaultObserver() { diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableEventStream.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableEventStream.java index c60f694209..48e637f5c3 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableEventStream.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableEventStream.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ public static Observable getEventStream(final String type, final int numI } public static Event randomEvent(String type, int numInstances) { - Map values = new LinkedHashMap(); + Map values = new LinkedHashMap<>(); values.put("count200", randomIntFrom0to(4000)); values.put("count4xx", randomIntFrom0to(300)); values.put("count5xx", randomIntFrom0to(500)); diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableFuseableTest.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableFuseableTest.java index 1d01a790ab..8de9b9bc0b 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableFuseableTest.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableFuseableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.observable; import java.util.Arrays; @@ -17,7 +18,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.TestHelper; public class ObservableFuseableTest extends RxJavaTest { diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableGroupByTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableGroupByTests.java index e5de6cdc85..1db6ebb908 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableGroupByTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableGroupByTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableMergeTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableMergeTests.java index 902bcd7ba1..25a3317b7f 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableMergeTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableMergeTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableNullTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableNullTests.java index 211053b0c7..178adb6230 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableNullTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableNullTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,7 +22,6 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -40,22 +39,6 @@ public class ObservableNullTests extends RxJavaTest { // Static methods //*********************************************************** - @Test(expected = NullPointerException.class) - public void ambVarargsNull() { - Observable.ambArray((Observable[])null); - } - - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void ambVarargsOneIsNull() { - Observable.ambArray(Observable.never(), null).blockingLast(); - } - - @Test(expected = NullPointerException.class) - public void ambIterableNull() { - Observable.amb((Iterable>)null); - } - @Test public void ambIterableIteratorNull() { Observable.amb(new Iterable>() { @@ -66,7 +49,6 @@ public Iterator> iterator() { }).test().assertError(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void ambIterableOneIsNull() { Observable.amb(Arrays.asList(Observable.never(), null)) @@ -74,16 +56,6 @@ public void ambIterableOneIsNull() { .assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void combineLatestIterableNull() { - Observable.combineLatest((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }, 128); - } - @Test(expected = NullPointerException.class) public void combineLatestIterableIteratorNull() { Observable.combineLatest(new Iterable>() { @@ -99,7 +71,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestIterableOneIsNull() { Observable.combineLatest(Arrays.asList(Observable.never(), null), new Function() { @@ -110,13 +81,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void combineLatestIterableFunctionNull() { - Observable.combineLatest(Arrays.asList(just1), null, 128); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestIterableFunctionReturnsNull() { Observable.combineLatest(Arrays.asList(just1), new Function() { @@ -127,16 +91,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void combineLatestDelayErrorIterableNull() { - Observable.combineLatestDelayError((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }, 128); - } - @Test(expected = NullPointerException.class) public void combineLatestDelayErrorIterableIteratorNull() { Observable.combineLatestDelayError(new Iterable>() { @@ -152,7 +106,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestDelayErrorIterableOneIsNull() { Observable.combineLatestDelayError(Arrays.asList(Observable.never(), null), new Function() { @@ -163,13 +116,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void combineLatestDelayErrorIterableFunctionNull() { - Observable.combineLatestDelayError(Arrays.asList(just1), null, 128); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void combineLatestDelayErrorIterableFunctionReturnsNull() { Observable.combineLatestDelayError(Arrays.asList(just1), new Function() { @@ -180,11 +126,6 @@ public Object apply(Object[] v) { }, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void concatIterableNull() { - Observable.concat((Iterable>)null); - } - @Test(expected = NullPointerException.class) public void concatIterableIteratorNull() { Observable.concat(new Iterable>() { @@ -195,38 +136,16 @@ public Iterator> iterator() { }).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void concatIterableOneIsNull() { Observable.concat(Arrays.asList(just1, null)).blockingLast(); } - @Test(expected = NullPointerException.class) - public void concatObservableNull() { - Observable.concat((Observable>)null); - } - - @Test(expected = NullPointerException.class) - public void concatArrayNull() { - Observable.concatArray((Observable[])null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void concatArrayOneIsNull() { Observable.concatArray(just1, null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void createNull() { - Observable.unsafeCreate(null); - } - - @Test(expected = NullPointerException.class) - public void deferFunctionNull() { - Observable.defer(null); - } - @Test(expected = NullPointerException.class) public void deferFunctionReturnsNull() { Observable.defer(new Supplier>() { @@ -237,11 +156,6 @@ public Observable get() { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void errorFunctionNull() { - Observable.error((Supplier)null); - } - @Test(expected = NullPointerException.class) public void errorFunctionReturnsNull() { Observable.error(new Supplier() { @@ -252,26 +166,11 @@ public Throwable get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void errorThrowableNull() { - Observable.error((Throwable)null); - } - - @Test(expected = NullPointerException.class) - public void fromArrayNull() { - Observable.fromArray((Object[])null); - } - @Test(expected = NullPointerException.class) public void fromArrayOneIsNull() { Observable.fromArray(1, null).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromCallableNull() { - Observable.fromCallable(null); - } - @Test(expected = NullPointerException.class) public void fromCallableReturnsNull() { Observable.fromCallable(new Callable() { @@ -282,56 +181,25 @@ public Object call() throws Exception { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromFutureNull() { - Observable.fromFuture(null); - } - @Test public void fromFutureReturnsNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); f.run(); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.fromFuture(f).subscribe(to); to.assertNoValues(); to.assertNotComplete(); to.assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void fromFutureTimedFutureNull() { - Observable.fromFuture(null, 1, TimeUnit.SECONDS); - } - - @Test(expected = NullPointerException.class) - public void fromFutureTimedUnitNull() { - Observable.fromFuture(new FutureTask(Functions.EMPTY_RUNNABLE, null), 1, null); - } - - @Test(expected = NullPointerException.class) - public void fromFutureTimedSchedulerNull() { - Observable.fromFuture(new FutureTask(Functions.EMPTY_RUNNABLE, null), 1, TimeUnit.SECONDS, null); - } - @Test(expected = NullPointerException.class) public void fromFutureTimedReturnsNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); f.run(); Observable.fromFuture(f, 1, TimeUnit.SECONDS).blockingLast(); } - @Test(expected = NullPointerException.class) - public void fromFutureSchedulerNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); - Observable.fromFuture(f, null); - } - - @Test(expected = NullPointerException.class) - public void fromIterableNull() { - Observable.fromIterable(null); - } - @Test(expected = NullPointerException.class) public void fromIterableIteratorNull() { Observable.fromIterable(new Iterable() { @@ -347,11 +215,6 @@ public void fromIterableValueNull() { Observable.fromIterable(Arrays.asList(1, null)).blockingLast(); } - @Test(expected = NullPointerException.class) - public void generateConsumerNull() { - Observable.generate(null); - } - @Test(expected = NullPointerException.class) public void generateConsumerEmitsNull() { Observable.generate(new Consumer>() { @@ -362,35 +225,6 @@ public void accept(Emitter s) { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void generateStateConsumerInitialStateNull() { - BiConsumer> generator = new BiConsumer>() { - @Override - public void accept(Integer s, Emitter o) { - o.onNext(1); - } - }; - Observable.generate(null, generator); - } - - @Test(expected = NullPointerException.class) - public void generateStateFunctionInitialStateNull() { - Observable.generate(null, new BiFunction, Object>() { - @Override - public Object apply(Object s, Emitter o) { o.onNext(1); return s; } - }); - } - - @Test(expected = NullPointerException.class) - public void generateStateConsumerNull() { - Observable.generate(new Supplier() { - @Override - public Integer get() { - return 1; - } - }, (BiConsumer>)null); - } - @Test public void generateConsumerStateNullAllowed() { BiConsumer> generator = new BiConsumer>() { @@ -420,64 +254,10 @@ public Object get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void generateConsumerDisposeNull() { - BiConsumer> generator = new BiConsumer>() { - @Override - public void accept(Integer s, Emitter o) { - o.onNext(1); - } - }; - Observable.generate(new Supplier() { - @Override - public Integer get() { - return 1; - } - }, generator, null); - } - - @Test(expected = NullPointerException.class) - public void generateFunctionDisposeNull() { - Observable.generate(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new BiFunction, Object>() { - @Override - public Object apply(Object s, Emitter o) { o.onNext(1); return s; } - }, null); - } - - @Test(expected = NullPointerException.class) - public void intervalUnitNull() { - Observable.interval(1, null); - } - public void intervalSchedulerNull() { Observable.interval(1, TimeUnit.SECONDS, null); } - @Test(expected = NullPointerException.class) - public void intervalPeriodUnitNull() { - Observable.interval(1, 1, null); - } - - @Test(expected = NullPointerException.class) - public void intervalPeriodSchedulerNull() { - Observable.interval(1, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void intervalRangeUnitNull() { - Observable.intervalRange(1, 1, 1, 1, null); - } - - @Test(expected = NullPointerException.class) - public void intervalRangeSchedulerNull() { - Observable.intervalRange(1, 1, 1, 1, TimeUnit.SECONDS, null); - } - @Test public void justNull() throws Exception { @SuppressWarnings("rawtypes") @@ -505,11 +285,6 @@ public void justNull() throws Exception { } } - @Test(expected = NullPointerException.class) - public void mergeIterableNull() { - Observable.merge((Iterable>)null, 128, 128); - } - @Test(expected = NullPointerException.class) public void mergeIterableIteratorNull() { Observable.merge(new Iterable>() { @@ -520,28 +295,11 @@ public Iterator> iterator() { }, 128, 128).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeIterableOneIsNull() { Observable.merge(Arrays.asList(just1, null), 128, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void mergeArrayNull() { - Observable.mergeArray(128, 128, (Observable[])null); - } - - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void mergeArrayOneIsNull() { - Observable.mergeArray(128, 128, just1, null).blockingLast(); - } - - @Test(expected = NullPointerException.class) - public void mergeDelayErrorIterableNull() { - Observable.mergeDelayError((Iterable>)null, 128, 128); - } - @Test(expected = NullPointerException.class) public void mergeDelayErrorIterableIteratorNull() { Observable.mergeDelayError(new Iterable>() { @@ -552,79 +310,11 @@ public Iterator> iterator() { }, 128, 128).blockingLast(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeDelayErrorIterableOneIsNull() { Observable.mergeDelayError(Arrays.asList(just1, null), 128, 128).blockingLast(); } - @Test(expected = NullPointerException.class) - public void mergeDelayErrorArrayNull() { - Observable.mergeArrayDelayError(128, 128, (Observable[])null); - } - - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void mergeDelayErrorArrayOneIsNull() { - Observable.mergeArrayDelayError(128, 128, just1, null).blockingLast(); - } - - @Test(expected = NullPointerException.class) - public void sequenceEqualFirstNull() { - Observable.sequenceEqual(null, just1); - } - - @Test(expected = NullPointerException.class) - public void sequenceEqualSecondNull() { - Observable.sequenceEqual(just1, null); - } - - @Test(expected = NullPointerException.class) - public void sequenceEqualComparatorNull() { - Observable.sequenceEqual(just1, just1, null); - } - - @Test(expected = NullPointerException.class) - public void switchOnNextNull() { - Observable.switchOnNext(null); - } - - @Test(expected = NullPointerException.class) - public void timerUnitNull() { - Observable.timer(1, null); - } - - @Test(expected = NullPointerException.class) - public void timerSchedulerNull() { - Observable.timer(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void usingResourceSupplierNull() { - Observable.using(null, new Function>() { - @Override - public Observable apply(Object d) { - return just1; - } - }, new Consumer() { - @Override - public void accept(Object d) { } - }); - } - - @Test(expected = NullPointerException.class) - public void usingObservableSupplierNull() { - Observable.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null, new Consumer() { - @Override - public void accept(Object d) { } - }); - } - @Test(expected = NullPointerException.class) public void usingObservableSupplierReturnsNull() { Observable.using(new Supplier() { @@ -643,31 +333,6 @@ public void accept(Object d) { } }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void usingDisposeNull() { - Observable.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new Function>() { - @Override - public Observable apply(Object d) { - return just1; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void zipIterableNull() { - Observable.zip((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void zipIterableIteratorNull() { Observable.zip(new Iterable>() { @@ -683,13 +348,6 @@ public Object apply(Object[] v) { }).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void zipIterableFunctionNull() { - Observable.zip(Arrays.asList(just1, just1), null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableFunctionReturnsNull() { Observable.zip(Arrays.asList(just1, just1), new Function() { @@ -700,16 +358,6 @@ public Object apply(Object[] a) { }).blockingLast(); } - @Test(expected = NullPointerException.class) - public void zipIterable2Null() { - Observable.zip((Iterable>)null, new Function() { - @Override - public Object apply(Object[] a) { - return 1; - } - }, true, 128); - } - @Test(expected = NullPointerException.class) public void zipIterable2IteratorNull() { Observable.zip(new Iterable>() { @@ -725,13 +373,6 @@ public Object apply(Object[] a) { }, true, 128).blockingLast(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void zipIterable2FunctionNull() { - Observable.zip(Arrays.asList(just1, just1), null, true, 128); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterable2FunctionReturnsNull() { Observable.zip(Arrays.asList(just1, just1), new Function() { @@ -746,26 +387,6 @@ public Object apply(Object[] a) { // Instance methods //************************************************************* - @Test(expected = NullPointerException.class) - public void allPredicateNull() { - just1.all(null); - } - - @Test(expected = NullPointerException.class) - public void ambWithNull() { - just1.ambWith(null); - } - - @Test(expected = NullPointerException.class) - public void anyPredicateNull() { - just1.any(null); - } - - @Test(expected = NullPointerException.class) - public void bufferSupplierNull() { - just1.buffer(1, 1, (Supplier>)null); - } - @Test(expected = NullPointerException.class) public void bufferSupplierReturnsNull() { just1.buffer(1, 1, new Supplier>() { @@ -776,21 +397,6 @@ public Collection get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void bufferTimedUnitNull() { - just1.buffer(1L, 1L, null); - } - - @Test(expected = NullPointerException.class) - public void bufferTimedSchedulerNull() { - just1.buffer(1L, 1L, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void bufferTimedSupplierNull() { - just1.buffer(1L, 1L, TimeUnit.SECONDS, Schedulers.single(), null); - } - @Test(expected = NullPointerException.class) public void bufferTimedSupplierReturnsNull() { just1.buffer(1L, 1L, TimeUnit.SECONDS, Schedulers.single(), new Supplier>() { @@ -801,21 +407,6 @@ public Collection get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void bufferOpenCloseOpenNull() { - just1.buffer(null, new Function>() { - @Override - public Observable apply(Object o) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void bufferOpenCloseCloseNull() { - just1.buffer(just1, (Function>)null); - } - @Test(expected = NullPointerException.class) public void bufferOpenCloseCloseReturnsNull() { just1.buffer(just1, new Function>() { @@ -826,16 +417,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void bufferBoundaryNull() { - just1.buffer((Observable)null); - } - - @Test(expected = NullPointerException.class) - public void bufferBoundarySupplierNull() { - just1.buffer(just1, (Supplier>)null); - } - @Test(expected = NullPointerException.class) public void bufferBoundarySupplierReturnsNull() { just1.buffer(just1, new Supplier>() { @@ -846,19 +427,6 @@ public Collection get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void castNull() { - just1.cast(null); - } - - @Test(expected = NullPointerException.class) - public void collectInitialSupplierNull() { - just1.collect((Supplier)null, new BiConsumer() { - @Override - public void accept(Integer a, Integer b) { } - }); - } - @Test(expected = NullPointerException.class) public void collectInitialSupplierReturnsNull() { just1.collect(new Supplier() { @@ -882,29 +450,6 @@ public Object get() { }, null); } - @Test(expected = NullPointerException.class) - public void collectIntoInitialNull() { - just1.collectInto(null, new BiConsumer() { - @Override - public void accept(Object a, Integer b) { } - }); - } - - @Test(expected = NullPointerException.class) - public void collectIntoCollectorNull() { - just1.collectInto(1, null); - } - - @Test(expected = NullPointerException.class) - public void composeNull() { - just1.compose(null); - } - - @Test(expected = NullPointerException.class) - public void concatMapNull() { - just1.concatMap(null); - } - @Test(expected = NullPointerException.class) public void concatMapReturnsNull() { just1.concatMap(new Function>() { @@ -915,11 +460,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void concatMapIterableNull() { - just1.concatMapIterable(null); - } - @Test(expected = NullPointerException.class) public void concatMapIterableReturnNull() { just1.concatMapIterable(new Function>() { @@ -945,21 +485,6 @@ public Iterator iterator() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void concatWithNull() { - just1.concatWith((ObservableSource)null); - } - - @Test(expected = NullPointerException.class) - public void containsNull() { - just1.contains(null); - } - - @Test(expected = NullPointerException.class) - public void debounceFunctionNull() { - just1.debounce(null); - } - @Test(expected = NullPointerException.class) public void debounceFunctionReturnsNull() { just1.debounce(new Function>() { @@ -970,26 +495,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void debounceTimedUnitNull() { - just1.debounce(1, null); - } - - @Test(expected = NullPointerException.class) - public void debounceTimedSchedulerNull() { - just1.debounce(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void defaultIfEmptyNull() { - just1.defaultIfEmpty(null); - } - - @Test(expected = NullPointerException.class) - public void delayWithFunctionNull() { - just1.delay(null); - } - @Test(expected = NullPointerException.class) public void delayWithFunctionReturnsNull() { just1.delay(new Function>() { @@ -1000,61 +505,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void delayTimedUnitNull() { - just1.delay(1, null); - } - - @Test(expected = NullPointerException.class) - public void delayTimedSchedulerNull() { - just1.delay(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionTimedUnitNull() { - just1.delaySubscription(1, null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionTimedSchedulerNull() { - just1.delaySubscription(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionOtherNull() { - just1.delaySubscription((Observable)null); - } - - @Test(expected = NullPointerException.class) - public void delaySubscriptionFunctionNull() { - just1.delaySubscription((Observable)null); - } - - @Test(expected = NullPointerException.class) - public void delayBothInitialSupplierNull() { - just1.delay(null, new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void delayBothInitialSupplierReturnsNull() { - just1.delay(null, new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void delayBothItemSupplierNull() { - just1.delay(just1, null); - } - @Test(expected = NullPointerException.class) public void delayBothItemSupplierReturnsNull() { just1.delay(just1 @@ -1066,21 +516,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void distinctFunctionNull() { - just1.distinct(null); - } - - @Test(expected = NullPointerException.class) - public void distinctSupplierNull() { - just1.distinct(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void distinctSupplierReturnsNull() { just1.distinct(new Function() { @@ -1106,16 +541,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void distinctUntilChangedFunctionNull() { - just1.distinctUntilChanged((Function)null); - } - - @Test(expected = NullPointerException.class) - public void distinctUntilChangedBiPredicateNull() { - just1.distinctUntilChanged((BiPredicate)null); - } - @Test public void distinctUntilChangedFunctionReturnsNull() { Observable.range(1, 2).distinctUntilChanged(new Function() { @@ -1126,84 +551,6 @@ public Object apply(Integer v) { }).test().assertResult(1); } - @Test(expected = NullPointerException.class) - public void doOnDisposeNull() { - just1.doOnDispose(null); - } - - @Test(expected = NullPointerException.class) - public void doOnCompleteNull() { - just1.doOnComplete(null); - } - - @Test(expected = NullPointerException.class) - public void doOnEachSupplierNull() { - just1.doOnEach((Consumer>)null); - } - - @Test(expected = NullPointerException.class) - public void doOnEachSubscriberNull() { - just1.doOnEach((Observer)null); - } - - @Test(expected = NullPointerException.class) - public void doOnErrorNull() { - just1.doOnError(null); - } - - @Test(expected = NullPointerException.class) - public void doOnLifecycleOnSubscribeNull() { - just1.doOnLifecycle(null, Functions.EMPTY_ACTION); - } - - @Test(expected = NullPointerException.class) - public void doOnLifecycleOnDisposeNull() { - just1.doOnLifecycle(new Consumer() { - @Override - public void accept(Disposable d) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void doOnNextNull() { - just1.doOnNext(null); - } - - @Test(expected = NullPointerException.class) - public void doOnSubscribeNull() { - just1.doOnSubscribe(null); - } - - @Test(expected = NullPointerException.class) - public void doOnTerminatedNull() { - just1.doOnTerminate(null); - } - - @Test(expected = NullPointerException.class) - public void elementAtNull() { - just1.elementAt(1, null); - } - - @Test(expected = NullPointerException.class) - public void filterNull() { - just1.filter(null); - } - - @Test(expected = NullPointerException.class) - public void doAfterTerminateNull() { - just1.doAfterTerminate(null); - } - - @Test(expected = NullPointerException.class) - public void firstNull() { - just1.first(null); - } - - @Test(expected = NullPointerException.class) - public void flatMapNull() { - just1.flatMap(null); - } - @Test(expected = NullPointerException.class) public void flatMapFunctionReturnsNull() { just1.flatMap(new Function>() { @@ -1214,21 +561,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapNotificationOnNextNull() { - just1.flatMap(null, new Function>() { - @Override - public Observable apply(Throwable e) { - return just1; - } - }, new Supplier>() { - @Override - public Observable get() { - return just1; - } - }); - } - @Test(expected = NullPointerException.class) public void flatMapNotificationOnNextReturnsNull() { just1.flatMap(new Function>() { @@ -1249,36 +581,6 @@ public Observable get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapNotificationOnErrorNull() { - just1.flatMap(new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }, null, new Supplier>() { - @Override - public Observable get() { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void flatMapNotificationOnCompleteNull() { - just1.flatMap(new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }, new Function>() { - @Override - public Observable apply(Throwable e) { - return just1; - } - }, null); - } - @Test(expected = NullPointerException.class) public void flatMapNotificationOnCompleteReturnsNull() { just1.flatMap(new Function>() { @@ -1299,16 +601,6 @@ public Observable get() { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapCombinerMapperNull() { - just1.flatMap(null, new BiFunction() { - @Override - public Object apply(Integer a, Object b) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void flatMapCombinerMapperReturnsNull() { just1.flatMap(new Function>() { @@ -1324,16 +616,6 @@ public Object apply(Integer a, Object b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapCombinerCombinerNull() { - just1.flatMap(new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }, null); - } - @Test(expected = NullPointerException.class) public void flatMapCombinerCombinerReturnsNull() { just1.flatMap(new Function>() { @@ -1349,11 +631,6 @@ public Object apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapIterableMapperNull() { - just1.flatMapIterable(null); - } - @Test(expected = NullPointerException.class) public void flatMapIterableMapperReturnsNull() { just1.flatMapIterable(new Function>() { @@ -1389,16 +666,6 @@ public Iterable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void flatMapIterableCombinerNull() { - just1.flatMapIterable(new Function>() { - @Override - public Iterable apply(Integer v) { - return Arrays.asList(1); - } - }, null); - } - @Test(expected = NullPointerException.class) public void flatMapIterableCombinerReturnsNull() { just1.flatMapIterable(new Function>() { @@ -1414,44 +681,6 @@ public Object apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void forEachNull() { - just1.forEach(null); - } - - @Test(expected = NullPointerException.class) - public void forEachWhileNull() { - just1.forEachWhile(null); - } - - @Test(expected = NullPointerException.class) - public void forEachWhileOnErrorNull() { - just1.forEachWhile(new Predicate() { - @Override - public boolean test(Integer v) { - return true; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void forEachWhileOnCompleteNull() { - just1.forEachWhile(new Predicate() { - @Override - public boolean test(Integer v) { - return true; - } - }, new Consumer() { - @Override - public void accept(Throwable e) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void groupByNull() { - just1.groupBy(null); - } - public void groupByKeyNull() { just1.groupBy(new Function() { @Override @@ -1461,16 +690,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void groupByValueNull() { - just1.groupBy(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void groupByValueReturnsNull() { just1.groupBy(new Function() { @@ -1486,16 +705,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void lastNull() { - just1.last(null); - } - - @Test(expected = NullPointerException.class) - public void liftNull() { - just1.lift(null); - } - @Test(expected = NullPointerException.class) public void liftReturnsNull() { just1.lift(new ObservableOperator() { @@ -1506,11 +715,6 @@ public Observer apply(Observer observer) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void mapNull() { - just1.map(null); - } - @Test(expected = NullPointerException.class) public void mapReturnsNull() { just1.map(new Function() { @@ -1521,26 +725,6 @@ public Object apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void mergeWithNull() { - just1.mergeWith((ObservableSource)null); - } - - @Test(expected = NullPointerException.class) - public void observeOnNull() { - just1.observeOn(null); - } - - @Test(expected = NullPointerException.class) - public void ofTypeNull() { - just1.ofType(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorResumeNextFunctionNull() { - just1.onErrorResumeNext(null); - } - @Test(expected = NullPointerException.class) public void onErrorResumeNextFunctionReturnsNull() { Observable.error(new TestException()).onErrorResumeNext(new Function>() { @@ -1551,21 +735,6 @@ public Observable apply(Throwable e) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void onErrorResumeNextObservableNull() { - just1.onErrorResumeWith(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorReturnFunctionNull() { - just1.onErrorReturn(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorReturnValueNull() { - just1.onErrorReturnItem(null); - } - @Test(expected = NullPointerException.class) public void onErrorReturnFunctionReturnsNull() { Observable.error(new TestException()).onErrorReturn(new Function() { @@ -1576,11 +745,6 @@ public Object apply(Throwable e) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void publishFunctionNull() { - just1.publish(null); - } - @Test(expected = NullPointerException.class) public void publishFunctionReturnsNull() { just1.publish(new Function, Observable>() { @@ -1591,11 +755,6 @@ public Observable apply(Observable v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void reduceFunctionNull() { - just1.reduce(null); - } - @Test(expected = NullPointerException.class) public void reduceFunctionReturnsNull() { Observable.just(1, 1).reduce(new BiFunction() { @@ -1606,21 +765,6 @@ public Integer apply(Integer a, Integer b) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void reduceSeedNull() { - just1.reduce(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void reduceSeedFunctionNull() { - just1.reduce(1, null); - } - @Test(expected = NullPointerException.class) public void reduceSeedFunctionReturnsNull() { just1.reduce(1, new BiFunction() { @@ -1631,16 +775,6 @@ public Integer apply(Integer a, Integer b) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void reduceWithSeedNull() { - just1.reduceWith(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void reduceWithSeedReturnsNull() { just1.reduceWith(new Supplier() { @@ -1656,16 +790,6 @@ public Object apply(Object a, Integer b) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void repeatUntilNull() { - just1.repeatUntil(null); - } - - @Test(expected = NullPointerException.class) - public void repeatWhenNull() { - just1.repeatWhen(null); - } - @Test(expected = NullPointerException.class) public void repeatWhenFunctionReturnsNull() { just1.repeatWhen(new Function, Observable>() { @@ -1676,11 +800,6 @@ public Observable apply(Observable v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void replaySelectorNull() { - just1.replay((Function, Observable>)null); - } - @Test(expected = NullPointerException.class) public void replaySelectorReturnsNull() { just1.replay(new Function, Observable>() { @@ -1691,11 +810,6 @@ public Observable apply(Observable o) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void replayBoundedSelectorNull() { - just1.replay((Function, Observable>)null, 1, 1, TimeUnit.SECONDS); - } - @Test(expected = NullPointerException.class) public void replayBoundedSelectorReturnsNull() { just1.replay(new Function, Observable>() { @@ -1706,31 +820,6 @@ public Observable apply(Observable v) { }, 1, 1, TimeUnit.SECONDS).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void replayBoundedUnitNull() { - just1.replay(new Function, Observable>() { - @Override - public Observable apply(Observable v) { - return v; - } - }, 1, 1, null).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void replayBoundedSchedulerNull() { - just1.replay(new Function, Observable>() { - @Override - public Observable apply(Observable v) { - return v; - } - }, 1, 1, TimeUnit.SECONDS, null).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedSelectorNull() { - just1.replay(null, 1, TimeUnit.SECONDS, Schedulers.single()); - } - @Test(expected = NullPointerException.class) public void replayTimeBoundedSelectorReturnsNull() { just1.replay(new Function, Observable>() { @@ -1741,66 +830,6 @@ public Observable apply(Observable v) { }, 1, TimeUnit.SECONDS, Schedulers.single()).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void replaySelectorTimeBoundedUnitNull() { - just1.replay(new Function, Observable>() { - @Override - public Observable apply(Observable v) { - return v; - } - }, 1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replaySelectorTimeBoundedSchedulerNull() { - just1.replay(new Function, Observable>() { - @Override - public Observable apply(Observable v) { - return v; - } - }, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void replayTimeSizeBoundedUnitNull() { - just1.replay(1, 1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replayTimeSizeBoundedSchedulerNull() { - just1.replay(1, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedUnitNull() { - just1.replay(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void replayTimeBoundedSchedulerNull() { - just1.replay(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void retryFunctionNull() { - just1.retry((BiPredicate)null); - } - - @Test(expected = NullPointerException.class) - public void retryCountFunctionNull() { - just1.retry(1, null); - } - - @Test(expected = NullPointerException.class) - public void retryPredicateNull() { - just1.retry((Predicate)null); - } - - @Test(expected = NullPointerException.class) - public void retryWhenFunctionNull() { - just1.retryWhen(null); - } - @Test(expected = NullPointerException.class) public void retryWhenFunctionReturnsNull() { Observable.error(new TestException()).retryWhen(new Function, Observable>() { @@ -1811,36 +840,6 @@ public Observable apply(Observable f) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void retryUntil() { - just1.retryUntil(null); - } - - @Test(expected = NullPointerException.class) - public void safeSubscribeNull() { - just1.safeSubscribe(null); - } - - @Test(expected = NullPointerException.class) - public void sampleUnitNull() { - just1.sample(1, null); - } - - @Test(expected = NullPointerException.class) - public void sampleSchedulerNull() { - just1.sample(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void sampleObservableNull() { - just1.sample(null); - } - - @Test(expected = NullPointerException.class) - public void scanFunctionNull() { - just1.scan(null); - } - @Test(expected = NullPointerException.class) public void scanFunctionReturnsNull() { Observable.just(1, 1).scan(new BiFunction() { @@ -1851,21 +850,6 @@ public Integer apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void scanSeedNull() { - just1.scan(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void scanSeedFunctionNull() { - just1.scan(1, null); - } - @Test(expected = NullPointerException.class) public void scanSeedFunctionReturnsNull() { just1.scan(1, new BiFunction() { @@ -1876,16 +860,6 @@ public Integer apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void scanSeedSupplierNull() { - just1.scanWith(null, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void scanSeedSupplierReturnsNull() { just1.scanWith(new Supplier() { @@ -1901,16 +875,6 @@ public Object apply(Object a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void scanSeedSupplierFunctionNull() { - just1.scanWith(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null); - } - @Test(expected = NullPointerException.class) public void scanSeedSupplierFunctionReturnsNull() { just1.scanWith(new Supplier() { @@ -1926,46 +890,6 @@ public Object apply(Object a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void singleNull() { - just1.single(null); - } - - @Test(expected = NullPointerException.class) - public void skipTimedUnitNull() { - just1.skip(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void skipTimedSchedulerNull() { - just1.skip(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void skipLastTimedUnitNull() { - just1.skipLast(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void skipLastTimedSchedulerNull() { - just1.skipLast(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void skipUntilNull() { - just1.skipUntil(null); - } - - @Test(expected = NullPointerException.class) - public void skipWhileNull() { - just1.skipWhile(null); - } - - @Test(expected = NullPointerException.class) - public void startWithIterableNull() { - just1.startWithIterable((Iterable)null); - } - @Test(expected = NullPointerException.class) public void startWithIterableIteratorNull() { just1.startWithIterable(new Iterable() { @@ -1981,70 +905,6 @@ public void startWithIterableOneNull() { just1.startWithIterable(Arrays.asList(1, null)).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void startWithSingleNull() { - just1.startWithItem((Integer)null); - } - - @Test(expected = NullPointerException.class) - public void startWithObservableNull() { - just1.startWith((Observable)null); - } - - @Test(expected = NullPointerException.class) - public void startWithArrayNull() { - just1.startWithArray((Integer[])null); - } - - @Test(expected = NullPointerException.class) - public void startWithArrayOneNull() { - just1.startWithArray(1, null).blockingSubscribe(); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnNextNull() { - just1.subscribe((Consumer)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnErrorNull() { - just1.subscribe(new Consumer() { - @Override - public void accept(Integer e) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnCompleteNull() { - just1.subscribe(new Consumer() { - @Override - public void accept(Integer e) { } - }, new Consumer() { - @Override - public void accept(Throwable e) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void subscribeNull() { - just1.subscribe((Observer)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnNull() { - just1.subscribeOn(null); - } - - @Test(expected = NullPointerException.class) - public void switchIfEmptyNull() { - just1.switchIfEmpty(null); - } - - @Test(expected = NullPointerException.class) - public void switchMapNull() { - just1.switchMap(null); - } - @Test(expected = NullPointerException.class) public void switchMapFunctionReturnsNull() { just1.switchMap(new Function>() { @@ -2055,96 +915,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void takeTimedUnitNull() { - just1.take(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void takeTimedSchedulerNull() { - just1.take(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void takeLastTimedUnitNull() { - just1.takeLast(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void takeLastSizeTimedUnitNull() { - just1.takeLast(1, 1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void takeLastTimedSchedulerNull() { - just1.takeLast(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void takeLastSizeTimedSchedulerNull() { - just1.takeLast(1, 1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void takeUntilPredicateNull() { - just1.takeUntil((Predicate)null); - } - - @Test(expected = NullPointerException.class) - public void takeUntilObservableNull() { - just1.takeUntil((Observable)null); - } - - @Test(expected = NullPointerException.class) - public void takeWhileNull() { - just1.takeWhile(null); - } - - @Test(expected = NullPointerException.class) - public void throttleFirstUnitNull() { - just1.throttleFirst(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void throttleFirstSchedulerNull() { - just1.throttleFirst(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void throttleLastUnitNull() { - just1.throttleLast(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void throttleLastSchedulerNull() { - just1.throttleLast(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void throttleWithTimeoutUnitNull() { - just1.throttleWithTimeout(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void throttleWithTimeoutSchedulerNull() { - just1.throttleWithTimeout(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void timeIntervalUnitNull() { - just1.timeInterval(null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void timeIntervalSchedulerNull() { - just1.timeInterval(TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void timeoutSelectorNull() { - just1.timeout(null); - } - @Test(expected = NullPointerException.class) public void timeoutSelectorReturnsNull() { just1.timeout(new Function>() { @@ -2155,46 +925,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void timeoutSelectorOtherNull() { - just1.timeout(new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void timeoutUnitNull() { - just1.timeout(1, null, Schedulers.single(), just1); - } - - @Test(expected = NullPointerException.class) - public void timeouOtherNull() { - just1.timeout(1, TimeUnit.SECONDS, Schedulers.single(), null); - } - - @Test(expected = NullPointerException.class) - public void timeouSchedulerNull() { - just1.timeout(1, TimeUnit.SECONDS, null, just1); - } - - @Test(expected = NullPointerException.class) - public void timeoutFirstNull() { - just1.timeout((Observable)null, new Function>() { - @Override - public Observable apply(Integer v) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void timeoutFirstItemNull() { - just1.timeout(just1, null); - } - @Test(expected = NullPointerException.class) public void timeoutFirstItemReturnsNull() { Observable.just(1, 1).timeout(Observable.never(), new Function>() { @@ -2205,26 +935,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void timestampUnitNull() { - just1.timestamp(null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void timestampSchedulerNull() { - just1.timestamp(TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void toNull() { - just1.to(null); - } - - @Test(expected = NullPointerException.class) - public void toListNull() { - just1.toList(null); - } - @Test(expected = NullPointerException.class) public void toListSupplierReturnsNull() { just1.toList(new Supplier>() { @@ -2235,26 +945,6 @@ public Collection get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toSortedListNull() { - just1.toSortedList(null); - } - - @Test(expected = NullPointerException.class) - public void toMapKeyNull() { - just1.toMap(null); - } - - @Test(expected = NullPointerException.class) - public void toMapValueNull() { - just1.toMap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test public void toMapValueSelectorReturnsNull() { just1.toMap(new Function() { @@ -2270,21 +960,6 @@ public Object apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMapMapSupplierNull() { - just1.toMap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void toMapMapSupplierReturnsNull() { just1.toMap(new Function() { @@ -2305,21 +980,6 @@ public Map get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMultimapKeyNull() { - just1.toMultimap(null); - } - - @Test(expected = NullPointerException.class) - public void toMultimapValueNull() { - just1.toMultimap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test public void toMultiMapValueSelectorReturnsNullAllowed() { just1.toMap(new Function() { @@ -2335,21 +995,6 @@ public Object apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMultimapMapMapSupplierNull() { - just1.toMultimap(new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, new Function() { - @Override - public Object apply(Integer v) { - return v; - } - }, null); - } - @Test(expected = NullPointerException.class) public void toMultimapMapSupplierReturnsNull() { just1.toMultimap(new Function() { @@ -2370,26 +1015,6 @@ public Map> get() { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void toMultimapMapMapCollectionSupplierNull() { - just1.toMultimap(new Function() { - @Override - public Integer apply(Integer v) { - return v; - } - }, new Function() { - @Override - public Integer apply(Integer v) { - return v; - } - }, new Supplier>>() { - @Override - public Map> get() { - return new HashMap>(); - } - }, null); - } - @Test(expected = NullPointerException.class) public void toMultimapMapCollectionSupplierReturnsNull() { just1.toMultimap(new Function() { @@ -2405,7 +1030,7 @@ public Integer apply(Integer v) { }, new Supplier>>() { @Override public Map> get() { - return new HashMap>(); + return new HashMap<>(); } }, new Function>() { @Override @@ -2415,56 +1040,6 @@ public Collection apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void unsafeSubscribeNull() { - just1.subscribe((Observer)null); - } - - @Test(expected = NullPointerException.class) - public void unsubscribeOnNull() { - just1.unsubscribeOn(null); - } - - @Test(expected = NullPointerException.class) - public void windowTimedUnitNull() { - just1.window(1, null, Schedulers.single()); - } - - @Test(expected = NullPointerException.class) - public void windowSizeTimedUnitNull() { - just1.window(1, null, Schedulers.single(), 1); - } - - @Test(expected = NullPointerException.class) - public void windowTimedSchedulerNull() { - just1.window(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void windowSizeTimedSchedulerNull() { - just1.window(1, TimeUnit.SECONDS, null, 1); - } - - @Test(expected = NullPointerException.class) - public void windowBoundaryNull() { - just1.window((Observable)null); - } - - @Test(expected = NullPointerException.class) - public void windowOpenCloseOpenNull() { - just1.window(null, new Function>() { - @Override - public Observable apply(Object v) { - return just1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void windowOpenCloseCloseNull() { - just1.window(just1, null); - } - @Test(expected = NullPointerException.class) public void windowOpenCloseCloseReturnsNull() { Observable.never().window(just1, new Function>() { @@ -2475,21 +1050,6 @@ public Observable apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void withLatestFromOtherNull() { - just1.withLatestFrom(null, new BiFunction() { - @Override - public Object apply(Integer a, Object b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void withLatestFromCombinerNull() { - just1.withLatestFrom(just1, null); - } - @Test(expected = NullPointerException.class) public void withLatestFromCombinerReturnsNull() { just1.withLatestFrom(just1, new BiFunction() { @@ -2500,21 +1060,6 @@ public Object apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void zipWithIterableNull() { - just1.zipWith((Iterable)null, new BiFunction() { - @Override - public Object apply(Integer a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void zipWithIterableCombinerNull() { - just1.zipWith(Arrays.asList(1), null); - } - @Test(expected = NullPointerException.class) public void zipWithIterableCombinerReturnsNull() { just1.zipWith(Arrays.asList(1), new BiFunction() { @@ -2550,21 +1095,6 @@ public Object apply(Integer a, Integer b) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void zipWithObservableNull() { - just1.zipWith((Observable)null, new BiFunction() { - @Override - public Object apply(Integer a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void zipWithCombinerNull() { - just1.zipWith(just1, null); - } - @Test(expected = NullPointerException.class) public void zipWithCombinerReturnsNull() { just1.zipWith(just1, new BiFunction() { diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableReduceTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableReduceTests.java index 8dec71b98e..01c2106aff 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableReduceTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableReduceTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableScanTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableScanTests.java index 195c7d5865..8bc33ac19f 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableScanTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableScanTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ public class ObservableScanTests extends RxJavaTest { public void unsubscribeScan() throws Exception { ObservableEventStream.getEventStream("HTTP-ClusterB", 20) - .scan(new HashMap(), new BiFunction, Event, HashMap>() { + .scan(new HashMap<>(), new BiFunction, Event, HashMap>() { @Override public HashMap apply(HashMap accum, Event perInstanceEvent) { accum.put("instance", perInstanceEvent.instanceId); diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableStartWithTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableStartWithTests.java index c8a195d826..8e0fe35add 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableStartWithTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableStartWithTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public void startWith1() { @Test public void startWithIterable() { - List li = new ArrayList(); + List li = new ArrayList<>(); li.add("alpha"); li.add("beta"); List values = Observable.just("one", "two").startWithIterable(li).toList().blockingGet(); @@ -48,7 +48,7 @@ public void startWithIterable() { @Test public void startWithObservable() { - List li = new ArrayList(); + List li = new ArrayList<>(); li.add("alpha"); li.add("beta"); List values = Observable.just("one", "two") diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableSubscriberTest.java index 6fc73f4468..c8a1047fe3 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -128,7 +128,7 @@ public void onNext(Integer t) { @Test public void subscribeConsumerConsumer() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.just(1).subscribe(new Consumer() { @Override @@ -147,7 +147,7 @@ public void accept(Throwable e) throws Exception { @Test public void subscribeConsumerConsumerWithError() { - final List list = new ArrayList(); + final List list = new ArrayList<>(); Observable.error(new TestException()).subscribe(new Consumer() { @Override @@ -175,8 +175,8 @@ public void methodTestCancelled() { @Test public void safeSubscriberAlreadySafe() { - TestObserver to = new TestObserver(); - Observable.just(1).safeSubscribe(new SafeObserver(to)); + TestObserver to = new TestObserver<>(); + Observable.just(1).safeSubscribe(new SafeObserver<>(to)); to.assertResult(1); } diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableTest.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableTest.java index 4d26afe20d..87e6463d3c 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableTest.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -66,7 +66,7 @@ public void fromArray() { @Test public void fromIterable() { - ArrayList items = new ArrayList(); + ArrayList items = new ArrayList<>(); items.add("one"); items.add("two"); items.add("three"); @@ -367,7 +367,7 @@ public void materializeDematerializeChaining() { public void customObservableWithErrorInObserverAsynchronous() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); // FIXME custom built??? Observable.just("1", "2", "three", "4") @@ -415,7 +415,7 @@ public void onNext(String v) { @Test public void customObservableWithErrorInObserverSynchronous() { final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); // FIXME custom built??? Observable.just("1", "2", "three", "4") @@ -458,7 +458,7 @@ public void onNext(String v) { @Test public void customObservableWithErrorInObservableSynchronous() { final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); // FIXME custom built??? Observable.just("1", "2").concatWith(Observable.error(new Supplier() { @Override @@ -500,7 +500,7 @@ public void publishLast() throws InterruptedException { ConnectableObservable connectable = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); count.incrementAndGet(); new Thread(new Runnable() { @Override @@ -538,7 +538,7 @@ public void replay() throws InterruptedException { ConnectableObservable o = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override @@ -591,7 +591,7 @@ public void cache() throws InterruptedException { Observable o = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override public void run() { @@ -636,7 +636,7 @@ public void cacheWithCapacity() throws InterruptedException { Observable o = Observable.unsafeCreate(new ObservableSource() { @Override public void subscribe(final Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); new Thread(new Runnable() { @Override public void run() { @@ -678,7 +678,7 @@ public void accept(String v) { @Test public void takeWithErrorInObserver() { final AtomicInteger count = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); Observable.just("1", "2", "three", "4").take(3) .safeSubscribe(new DefaultObserver() { @@ -729,9 +729,9 @@ public void ofType() { @Test public void ofTypeWithPolymorphism() { - ArrayList l1 = new ArrayList(); + ArrayList l1 = new ArrayList<>(); l1.add(1); - LinkedList l2 = new LinkedList(); + LinkedList l2 = new LinkedList<>(); l2.add(2); @SuppressWarnings("rawtypes") @@ -921,21 +921,21 @@ public void rangeWithScheduler() { @Test public void mergeWith() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1).mergeWith(Observable.just(2)).subscribe(to); to.assertValues(1, 2); } @Test public void concatWith() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1).concatWith(Observable.just(2)).subscribe(to); to.assertValues(1, 2); } @Test public void ambWith() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1).ambWith(Observable.just(2)).subscribe(to); to.assertValue(1); } @@ -967,7 +967,7 @@ public void accept(List booleans) { @Test public void compose() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1, 2, 3).compose(new ObservableTransformer() { @Override @@ -1039,16 +1039,9 @@ public void emptyIsEmpty() { // }}); // } - @Test(expected = NullPointerException.class) - public void forEachWithNull() { - Observable.error(new Exception("boo")) - // - .forEach(null); - } - @Test public void extend() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final Object value = new Object(); Object returned = Observable.just(value).to(new ObservableConverter() { @Override @@ -1065,7 +1058,7 @@ public Object apply(Observable onSubscribe) { @Test public void asExtend() { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final Object value = new Object(); Object returned = Observable.just(value).to(new ObservableConverter() { @Override @@ -1120,7 +1113,6 @@ public void singleDefaultObservable() { @Test public void zipIterableObject() { - @SuppressWarnings("unchecked") final List> observables = Arrays.asList(Observable.just(1, 2, 3), Observable.just(1, 2, 3)); Observable.zip(observables, new Function() { @Override @@ -1136,7 +1128,6 @@ public Object apply(Object[] o) throws Exception { @Test public void combineLatestObject() { - @SuppressWarnings("unchecked") final List> observables = Arrays.asList(Observable.just(1, 2, 3), Observable.just(1, 2, 3)); Observable.combineLatest(observables, new Function() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleLastTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleLastTests.java index e93f9409e0..d8107cc434 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleLastTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleLastTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,10 +13,10 @@ package io.reactivex.rxjava3.observable; -import static org.mockito.Mockito.inOrder; - import java.util.concurrent.TimeUnit; +import io.reactivex.rxjava3.exceptions.TestException; +import io.reactivex.rxjava3.functions.Action; import org.junit.Test; import org.mockito.InOrder; @@ -25,8 +25,78 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import io.reactivex.rxjava3.testsupport.TestHelper; +import static org.mockito.Mockito.*; + public class ObservableThrottleLastTests extends RxJavaTest { + @Test + public void throttleLastWithDropCallbackException() throws Throwable { + Observer observer = TestHelper.mockObserver(); + + Action whenDisposed = mock(Action.class); + + TestScheduler s = new TestScheduler(); + PublishSubject o = PublishSubject.create(); + o.doOnDispose(whenDisposed) + .throttleLast(500, TimeUnit.MILLISECONDS, s, e -> { + if (e == 1) { + throw new TestException("Forced"); + } + }) + .subscribe(observer); + + // send events with simulated time increments + s.advanceTimeTo(0, TimeUnit.MILLISECONDS); + o.onNext(1); // skip + o.onNext(2); // try to deliver + s.advanceTimeTo(501, TimeUnit.MILLISECONDS); + + InOrder inOrder = inOrder(observer); + inOrder.verify(observer).onError(any(TestException.class)); + inOrder.verifyNoMoreInteractions(); + verify(whenDisposed).run(); + } + + @Test + public void throttleLastWithDropCallback() { + Observer observer = TestHelper.mockObserver(); + + Observer dropCallbackObserver = TestHelper.mockObserver(); + + TestScheduler s = new TestScheduler(); + PublishSubject o = PublishSubject.create(); + o.throttleLast(500, TimeUnit.MILLISECONDS, s, dropCallbackObserver::onNext).subscribe(observer); + + // send events with simulated time increments + s.advanceTimeTo(0, TimeUnit.MILLISECONDS); + o.onNext(1); // skip + o.onNext(2); // deliver + s.advanceTimeTo(501, TimeUnit.MILLISECONDS); + o.onNext(3); // skip + s.advanceTimeTo(600, TimeUnit.MILLISECONDS); + o.onNext(4); // skip + s.advanceTimeTo(700, TimeUnit.MILLISECONDS); + o.onNext(5); // skip + o.onNext(6); // deliver + s.advanceTimeTo(1001, TimeUnit.MILLISECONDS); + o.onNext(7); // deliver + s.advanceTimeTo(1501, TimeUnit.MILLISECONDS); + o.onComplete(); + + InOrder inOrder = inOrder(observer); + InOrder dropCallbackOrder = inOrder(dropCallbackObserver); + dropCallbackOrder.verify(dropCallbackObserver).onNext(1); + inOrder.verify(observer).onNext(2); + dropCallbackOrder.verify(dropCallbackObserver).onNext(3); + dropCallbackOrder.verify(dropCallbackObserver).onNext(4); + dropCallbackOrder.verify(dropCallbackObserver).onNext(5); + inOrder.verify(observer).onNext(6); + inOrder.verify(observer).onNext(7); + inOrder.verify(observer).onComplete(); + inOrder.verifyNoMoreInteractions(); + dropCallbackOrder.verifyNoMoreInteractions(); + } + @Test public void throttle() { Observer observer = TestHelper.mockObserver(); diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleWithTimeoutTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleWithTimeoutTests.java index bcacab5513..80f82fb5f4 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleWithTimeoutTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableThrottleWithTimeoutTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableWindowTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableWindowTests.java index 1d4a62f7f3..4bc561bd1b 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableWindowTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableWindowTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,7 +31,7 @@ public class ObservableWindowTests extends RxJavaTest { @Test public void window() { - final ArrayList> lists = new ArrayList>(); + final ArrayList> lists = new ArrayList<>(); Observable.concat( Observable.just(1, 2, 3, 4, 5, 6) diff --git a/src/test/java/io/reactivex/rxjava3/observable/ObservableZipTests.java b/src/test/java/io/reactivex/rxjava3/observable/ObservableZipTests.java index b630807758..b2b26719bb 100644 --- a/src/test/java/io/reactivex/rxjava3/observable/ObservableZipTests.java +++ b/src/test/java/io/reactivex/rxjava3/observable/ObservableZipTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public String apply(Event e) { .flatMap(new Function, Observable>>() { @Override public Observable> apply(final GroupedObservable ge) { - return ge.scan(new HashMap(), new BiFunction, Event, HashMap>() { + return ge.scan(new HashMap<>(), new BiFunction, Event, HashMap>() { @Override public HashMap apply(HashMap accum, Event perInstanceEvent) { diff --git a/src/test/java/io/reactivex/rxjava3/observers/DisposableCompletableObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/DisposableCompletableObserverTest.java index 45d4b82172..fde1dc75de 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/DisposableCompletableObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/DisposableCompletableObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -33,7 +33,7 @@ static final class TestCompletable extends DisposableCompletableObserver { int complete; - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); @Override protected void onStart() { @@ -79,9 +79,9 @@ public void startOnce() { try { TestCompletable tc = new TestCompletable(); - tc.onSubscribe(Disposables.empty()); + tc.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); @@ -102,7 +102,7 @@ public void dispose() { assertTrue(tc.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/DisposableMaybeObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/DisposableMaybeObserverTest.java index 812e40ca3a..7bd2f20a95 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/DisposableMaybeObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/DisposableMaybeObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,9 +31,9 @@ static final class TestMaybe extends DisposableMaybeObserver { int start; - final List values = new ArrayList(); + final List values = new ArrayList<>(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int complete; @@ -62,7 +62,7 @@ public void onComplete() { @Test public void normal() { - TestMaybe tc = new TestMaybe(); + TestMaybe tc = new TestMaybe<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -85,11 +85,11 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestMaybe tc = new TestMaybe(); + TestMaybe tc = new TestMaybe<>(); - tc.onSubscribe(Disposables.empty()); + tc.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); @@ -105,12 +105,12 @@ public void startOnce() { @Test public void dispose() { - TestMaybe tc = new TestMaybe(); + TestMaybe tc = new TestMaybe<>(); tc.dispose(); assertTrue(tc.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/DisposableObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/DisposableObserverTest.java index 6a2b332293..1b5b477552 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/DisposableObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/DisposableObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,9 +32,9 @@ static final class TestDisposableObserver extends DisposableObserver { int start; - final List values = new ArrayList(); + final List values = new ArrayList<>(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int completions; @@ -63,7 +63,7 @@ public void onComplete() { @Test public void normal() { - TestDisposableObserver tc = new TestDisposableObserver(); + TestDisposableObserver tc = new TestDisposableObserver<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -84,11 +84,11 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestDisposableObserver tc = new TestDisposableObserver(); + TestDisposableObserver tc = new TestDisposableObserver<>(); - tc.onSubscribe(Disposables.empty()); + tc.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); @@ -104,7 +104,7 @@ public void startOnce() { @Test public void dispose() { - TestDisposableObserver tc = new TestDisposableObserver(); + TestDisposableObserver tc = new TestDisposableObserver<>(); assertFalse(tc.isDisposed()); @@ -112,7 +112,7 @@ public void dispose() { assertTrue(tc.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/DisposableSingleObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/DisposableSingleObserverTest.java index 983a8dc847..8dac144591 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/DisposableSingleObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/DisposableSingleObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,9 +31,9 @@ static final class TestSingle extends DisposableSingleObserver { int start; - final List values = new ArrayList(); + final List values = new ArrayList<>(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); @Override protected void onStart() { @@ -56,7 +56,7 @@ public void onError(Throwable e) { @Test public void normal() { - TestSingle tc = new TestSingle(); + TestSingle tc = new TestSingle<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -77,11 +77,11 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestSingle tc = new TestSingle(); + TestSingle tc = new TestSingle<>(); - tc.onSubscribe(Disposables.empty()); + tc.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); @@ -97,12 +97,12 @@ public void startOnce() { @Test public void dispose() { - TestSingle tc = new TestSingle(); + TestSingle tc = new TestSingle<>(); tc.dispose(); assertTrue(tc.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/ResourceCompletableObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/ResourceCompletableObserverTest.java index 487f42352a..e2d1fa34ba 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/ResourceCompletableObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/ResourceCompletableObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ public class ResourceCompletableObserverTest extends RxJavaTest { static final class TestResourceCompletableObserver extends ResourceCompletableObserver { - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int complete; @@ -68,7 +68,7 @@ public void addResources() { assertFalse(rco.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rco.add(d); @@ -93,7 +93,7 @@ public void onCompleteCleansUp() { assertFalse(rco.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rco.add(d); @@ -112,7 +112,7 @@ public void onErrorCleansUp() { assertFalse(rco.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rco.add(d); @@ -167,9 +167,9 @@ public void startOnce() { try { TestResourceCompletableObserver rco = new TestResourceCompletableObserver(); - rco.onSubscribe(Disposables.empty()); + rco.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rco.onSubscribe(d); @@ -188,7 +188,7 @@ public void dispose() { TestResourceCompletableObserver rco = new TestResourceCompletableObserver(); rco.dispose(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rco.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/ResourceMaybeObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/ResourceMaybeObserverTest.java index 92370a3203..257a5785f8 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/ResourceMaybeObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/ResourceMaybeObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class ResourceMaybeObserverTest extends RxJavaTest { static final class TestResourceMaybeObserver extends ResourceMaybeObserver { T value; - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int complete; @@ -67,17 +67,17 @@ public void onError(Throwable e) { @Test(expected = NullPointerException.class) public void nullResource() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); rmo.add(null); } @Test public void addResources() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rmo.add(d); @@ -98,11 +98,11 @@ public void addResources() { @Test public void onCompleteCleansUp() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rmo.add(d); @@ -117,11 +117,11 @@ public void onCompleteCleansUp() { @Test public void onSuccessCleansUp() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rmo.add(d); @@ -136,11 +136,11 @@ public void onSuccessCleansUp() { @Test public void onErrorCleansUp() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rmo.add(d); @@ -155,7 +155,7 @@ public void onErrorCleansUp() { @Test public void normal() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); assertEquals(0, rmo.start); @@ -173,7 +173,7 @@ public void normal() { @Test public void empty() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); assertEquals(0, rmo.start); @@ -191,7 +191,7 @@ public void empty() { @Test public void error() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); assertFalse(rmo.isDisposed()); assertEquals(0, rmo.start); @@ -215,11 +215,11 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); - rmo.onSubscribe(Disposables.empty()); + rmo.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rmo.onSubscribe(d); @@ -235,10 +235,10 @@ public void startOnce() { @Test public void dispose() { - TestResourceMaybeObserver rmo = new TestResourceMaybeObserver(); + TestResourceMaybeObserver rmo = new TestResourceMaybeObserver<>(); rmo.dispose(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rmo.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/ResourceObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/ResourceObserverTest.java index d81b7fbe88..543095582b 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/ResourceObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/ResourceObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,9 +30,9 @@ public class ResourceObserverTest extends RxJavaTest { static final class TestResourceObserver extends ResourceObserver { - final List values = new ArrayList(); + final List values = new ArrayList<>(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int complete; @@ -67,17 +67,17 @@ public void onComplete() { @Test(expected = NullPointerException.class) public void nullResource() { - TestResourceObserver ro = new TestResourceObserver(); + TestResourceObserver ro = new TestResourceObserver<>(); ro.add(null); } @Test public void addResources() { - TestResourceObserver ro = new TestResourceObserver(); + TestResourceObserver ro = new TestResourceObserver<>(); assertFalse(ro.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ro.add(d); @@ -98,11 +98,11 @@ public void addResources() { @Test public void onCompleteCleansUp() { - TestResourceObserver ro = new TestResourceObserver(); + TestResourceObserver ro = new TestResourceObserver<>(); assertFalse(ro.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ro.add(d); @@ -117,11 +117,11 @@ public void onCompleteCleansUp() { @Test public void onErrorCleansUp() { - TestResourceObserver ro = new TestResourceObserver(); + TestResourceObserver ro = new TestResourceObserver<>(); assertFalse(ro.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ro.add(d); @@ -136,7 +136,7 @@ public void onErrorCleansUp() { @Test public void normal() { - TestResourceObserver tc = new TestResourceObserver(); + TestResourceObserver tc = new TestResourceObserver<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -153,7 +153,7 @@ public void normal() { @Test public void error() { - TestResourceObserver tc = new TestResourceObserver(); + TestResourceObserver tc = new TestResourceObserver<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -176,11 +176,11 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestResourceObserver tc = new TestResourceObserver(); + TestResourceObserver tc = new TestResourceObserver<>(); - tc.onSubscribe(Disposables.empty()); + tc.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); @@ -196,10 +196,10 @@ public void startOnce() { @Test public void dispose() { - TestResourceObserver tc = new TestResourceObserver(); + TestResourceObserver tc = new TestResourceObserver<>(); tc.dispose(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); tc.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/ResourceSingleObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/ResourceSingleObserverTest.java index 1add001b63..10f17e18d6 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/ResourceSingleObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/ResourceSingleObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class ResourceSingleObserverTest extends RxJavaTest { static final class TestResourceSingleObserver extends ResourceSingleObserver { T value; - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int start; @@ -58,17 +58,17 @@ public void onError(Throwable e) { @Test(expected = NullPointerException.class) public void nullResource() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); rso.add(null); } @Test public void addResources() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); assertFalse(rso.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rso.add(d); @@ -89,11 +89,11 @@ public void addResources() { @Test public void onSuccessCleansUp() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); assertFalse(rso.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rso.add(d); @@ -108,11 +108,11 @@ public void onSuccessCleansUp() { @Test public void onErrorCleansUp() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); assertFalse(rso.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rso.add(d); @@ -127,7 +127,7 @@ public void onErrorCleansUp() { @Test public void normal() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); assertFalse(rso.isDisposed()); assertEquals(0, rso.start); @@ -144,7 +144,7 @@ public void normal() { @Test public void error() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); assertFalse(rso.isDisposed()); assertEquals(0, rso.start); @@ -167,11 +167,11 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); - rso.onSubscribe(Disposables.empty()); + rso.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rso.onSubscribe(d); @@ -187,10 +187,10 @@ public void startOnce() { @Test public void dispose() { - TestResourceSingleObserver rso = new TestResourceSingleObserver(); + TestResourceSingleObserver rso = new TestResourceSingleObserver<>(); rso.dispose(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); rso.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java index 876bcffbe4..77b3d7b9b9 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/SafeObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ public class SafeObserverTest extends RxJavaTest { @Test public void onNextFailure() { - AtomicReference onError = new AtomicReference(); + AtomicReference onError = new AtomicReference<>(); try { OBSERVER_ONNEXT_FAIL(onError).onNext("one"); fail("expects exception to be thrown"); @@ -43,10 +43,10 @@ public void onNextFailure() { @Test public void onNextFailureSafe() { - AtomicReference onError = new AtomicReference(); + AtomicReference onError = new AtomicReference<>(); try { - SafeObserver safeObserver = new SafeObserver(OBSERVER_ONNEXT_FAIL(onError)); - safeObserver.onSubscribe(Disposables.empty()); + SafeObserver safeObserver = new SafeObserver<>(OBSERVER_ONNEXT_FAIL(onError)); + safeObserver.onSubscribe(Disposable.empty()); safeObserver.onNext("one"); assertNotNull(onError.get()); assertTrue(onError.get() instanceof SafeObserverTestException); @@ -58,7 +58,7 @@ public void onNextFailureSafe() { @Test public void onCompleteFailure() { - AtomicReference onError = new AtomicReference(); + AtomicReference onError = new AtomicReference<>(); try { OBSERVER_ONCOMPLETED_FAIL(onError).onComplete(); fail("expects exception to be thrown"); @@ -198,18 +198,18 @@ public void onError(Throwable e) { public void onComplete() { } }; - SafeObserver observer = new SafeObserver(actual); + SafeObserver observer = new SafeObserver<>(actual); assertSame(actual, observer.downstream); } @Test public void dispose() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -221,12 +221,13 @@ public void dispose() { } @Test + @SuppressUndeliverable public void onNextAfterComplete() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -243,11 +244,11 @@ public void onNextAfterComplete() { @Test public void onNextNull() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -258,9 +259,9 @@ public void onNextNull() { @Test public void onNextWithoutOnSubscribe() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); so.onNext(1); @@ -269,9 +270,9 @@ public void onNextWithoutOnSubscribe() { @Test public void onErrorWithoutOnSubscribe() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); so.onError(new TestException()); @@ -283,9 +284,9 @@ public void onErrorWithoutOnSubscribe() { @Test public void onCompleteWithoutOnSubscribe() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); so.onComplete(); @@ -294,11 +295,11 @@ public void onCompleteWithoutOnSubscribe() { @Test public void onNextNormal() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - SafeObserver so = new SafeObserver(to); + SafeObserver so = new SafeObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -369,7 +370,7 @@ public void onComplete() { } public SafeObserver toSafe() { - return new SafeObserver(this); + return new SafeObserver<>(this); } public CrashDummy assertError(Class clazz) { diff --git a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java index 1d467242f5..689908f59f 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/SerializedObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,7 @@ import org.junit.*; import io.reactivex.rxjava3.core.*; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -41,7 +41,7 @@ public void before() { } private Observer serializedObserver(Observer o) { - return new SerializedObserver(o); + return new SerializedObserver<>(o); } @Test @@ -163,7 +163,7 @@ public void runOutOfOrderConcurrencyTest() { try { TestConcurrencySubscriber tw = new TestConcurrencySubscriber(); // we need Synchronized + SafeObserver to handle synchronization plus life-cycle - Observer w = serializedObserver(new SafeObserver(tw)); + Observer w = serializedObserver(new SafeObserver<>(tw)); Future f1 = tp.submit(new OnNextThread(w, 12000)); Future f2 = tp.submit(new OnNextThread(w, 5000)); @@ -219,8 +219,8 @@ public void runConcurrencyTest() { try { TestConcurrencySubscriber tw = new TestConcurrencySubscriber(); // we need Synchronized + SafeObserver to handle synchronization plus life-cycle - Observer w = serializedObserver(new SafeObserver(tw)); - w.onSubscribe(Disposables.empty()); + Observer w = serializedObserver(new SafeObserver<>(tw)); + w.onSubscribe(Disposable.empty()); Future f1 = tp.submit(new OnNextThread(w, 12000)); Future f2 = tp.submit(new OnNextThread(w, 5000)); @@ -275,7 +275,7 @@ public void notificationDelay() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch running = new CountDownLatch(2); - TestObserverEx to = new TestObserverEx(new DefaultObserver() { + TestObserverEx to = new TestObserverEx<>(new DefaultObserver() { @Override public void onComplete() { @@ -340,11 +340,11 @@ public void onNext(String t) { * * When using SynchronizedSubscriber we get this output: * - * p1: 18 p2: 68 => should be close to each other unless we have thread starvation + * {@code p1: 18 p2: 68 =>} should be close to each other unless we have thread starvation * * When using SerializedObserver we get: * - * p1: 1 p2: 2445261 => should be close to each other unless we have thread starvation + * {@code p1: 1 p2: 2445261 =>} should be close to each other unless we have thread starvation * * This demonstrates how SynchronizedSubscriber balances back and forth better, and blocks emission. * The real issue in this example is the async buffer-bloat, so we need backpressure. @@ -356,7 +356,7 @@ public void onNext(String t) { @Test public void threadStarvation() throws InterruptedException { - TestObserver to = new TestObserver(new DefaultObserver() { + TestObserver to = new TestObserver<>(new DefaultObserver() { @Override public void onComplete() { @@ -383,7 +383,7 @@ public void onNext(String t) { AtomicInteger p1 = new AtomicInteger(); AtomicInteger p2 = new AtomicInteger(); - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); DisposableObserver as1 = new DisposableObserver() { @Override public void onNext(String t) { @@ -446,7 +446,7 @@ private static Observable infinite(final AtomicInteger produced) { @Override public void subscribe(Observer observer) { - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); observer.onSubscribe(bs); while (!bs.isDisposed()) { observer.onNext("onNext"); @@ -552,7 +552,7 @@ private static class TestConcurrencySubscriber extends DefaultObserver { /** * used to store the order and number of events received. */ - private final LinkedBlockingQueue events = new LinkedBlockingQueue(); + private final LinkedBlockingQueue events = new LinkedBlockingQueue<>(); private final int waitTime; @SuppressWarnings("unused") @@ -651,7 +651,7 @@ private static class TestSingleThreadedObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); System.out.println("TestSingleThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @@ -703,7 +703,7 @@ private static class TestMultiThreadedObservable implements ObservableSource observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); final NullPointerException npe = new NullPointerException(); System.out.println("TestMultiThreadedObservable subscribed to ..."); t = new Thread(new Runnable() { @@ -848,7 +848,7 @@ protected void captureMaxThreads() { public void errorReentry() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> serial = new AtomicReference>(); + final AtomicReference> serial = new AtomicReference<>(); TestObserver to = new TestObserver() { @Override @@ -858,8 +858,8 @@ public void onNext(Integer v) { super.onNext(v); } }; - SerializedObserver sobs = new SerializedObserver(to); - sobs.onSubscribe(Disposables.empty()); + SerializedObserver sobs = new SerializedObserver<>(to); + sobs.onSubscribe(Disposable.empty()); serial.set(sobs); sobs.onNext(1); @@ -875,7 +875,7 @@ public void onNext(Integer v) { @Test public void completeReentry() { - final AtomicReference> serial = new AtomicReference>(); + final AtomicReference> serial = new AtomicReference<>(); TestObserver to = new TestObserver() { @Override @@ -885,8 +885,8 @@ public void onNext(Integer v) { super.onNext(v); } }; - SerializedObserver sobs = new SerializedObserver(to); - sobs.onSubscribe(Disposables.empty()); + SerializedObserver sobs = new SerializedObserver<>(to); + sobs.onSubscribe(Disposable.empty()); serial.set(sobs); sobs.onNext(1); @@ -898,11 +898,11 @@ public void onNext(Integer v) { @Test public void dispose() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - SerializedObserver so = new SerializedObserver(to); + SerializedObserver so = new SerializedObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -918,11 +918,11 @@ public void dispose() { @Test public void onCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final SerializedObserver so = new SerializedObserver(to); + final SerializedObserver so = new SerializedObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -944,11 +944,11 @@ public void run() { @Test public void onNextOnCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final SerializedObserver so = new SerializedObserver(to); + final SerializedObserver so = new SerializedObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -980,11 +980,11 @@ public void run() { @Test public void onNextOnErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final SerializedObserver so = new SerializedObserver(to); + final SerializedObserver so = new SerializedObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -1018,11 +1018,11 @@ public void run() { @Test public void onNextOnErrorRaceDelayError() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final SerializedObserver so = new SerializedObserver(to, true); + final SerializedObserver so = new SerializedObserver<>(to, true); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -1059,13 +1059,13 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final SerializedObserver so = new SerializedObserver(to); + final SerializedObserver so = new SerializedObserver<>(to); - so.onSubscribe(Disposables.empty()); + so.onSubscribe(Disposable.empty()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -1083,11 +1083,11 @@ public void onCompleteOnErrorRace() { List errors = TestHelper.trackPluginErrors(); try { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - final SerializedObserver so = new SerializedObserver(to); + final SerializedObserver so = new SerializedObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -1130,11 +1130,11 @@ public void run() { @Test public void nullOnNext() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - final SerializedObserver so = new SerializedObserver(to); + final SerializedObserver so = new SerializedObserver<>(to); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); so.onSubscribe(d); @@ -1142,4 +1142,29 @@ public void nullOnNext() { to.assertFailureAndMessage(NullPointerException.class, ExceptionHelper.nullWarning("onNext called with a null value.")); } + + @Test + @SuppressUndeliverable + public void onErrorQueuedUp() { + AtomicReference> soRef = new AtomicReference<>(); + TestObserverEx to = new TestObserverEx() { + @Override + public void onNext(Integer t) { + super.onNext(t); + soRef.get().onNext(2); + soRef.get().onError(new TestException()); + } + }; + + final SerializedObserver so = new SerializedObserver<>(to, true); + soRef.set(so); + + Disposable d = Disposable.empty(); + + so.onSubscribe(d); + + so.onNext(1); + + to.assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java b/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java index 1468dc9cb5..f2d5f206c3 100644 --- a/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java +++ b/src/test/java/io/reactivex/rxjava3/observers/TestObserverTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,15 +20,15 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import org.junit.*; -import org.junit.rules.ExpectedException; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; import org.mockito.InOrder; import org.reactivestreams.Subscriber; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Predicate; import io.reactivex.rxjava3.internal.functions.Functions; @@ -40,13 +40,20 @@ public class TestObserverTest extends RxJavaTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); + static void assertThrowsWithMessage(String message, Class clazz, ThrowingRunnable run) { + assertEquals(message, assertThrows(clazz, run).getMessage()); + } + + static void assertThrowsWithMessageMatchRegex(String regex, Class clazz, ThrowingRunnable run) { + assertTrue(assertThrows(clazz, run).getMessage().matches(regex)); + } + + private static final String ASSERT_MESSAGE_REGEX = "\nexpected: (.*)\n\\s*got: (.*)"; @Test public void assertTestObserver() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriber subscriber = new TestSubscriber(); + TestSubscriber subscriber = new TestSubscriber<>(); oi.subscribe(subscriber); subscriber.assertValues(1, 2); @@ -56,50 +63,44 @@ public void assertTestObserver() { @Test public void assertNotMatchCount() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriber subscriber = new TestSubscriber(); - oi.subscribe(subscriber); - - thrown.expect(AssertionError.class); - // FIXME different message format -// thrown.expectMessage("Number of items does not match. Provided: 1 Actual: 2"); - - subscriber.assertValue(1); - subscriber.assertValueCount(2); - subscriber.assertComplete().assertNoErrors(); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriber subscriber = new TestSubscriber<>(); + oi.subscribe(subscriber); + + subscriber.assertValue(1); + subscriber.assertValueCount(2); + subscriber.assertComplete().assertNoErrors(); + }); } @Test public void assertNotMatchValue() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriber subscriber = new TestSubscriber(); - oi.subscribe(subscriber); - - thrown.expect(AssertionError.class); - // FIXME different message format -// thrown.expectMessage("Value at index: 1 expected to be [3] (Integer) but was: [2] (Integer)"); - - subscriber.assertValues(1, 3); - subscriber.assertValueCount(2); - subscriber.assertComplete().assertNoErrors(); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriber subscriber = new TestSubscriber<>(); + oi.subscribe(subscriber); + + subscriber.assertValues(1, 3); + subscriber.assertValueCount(2); + subscriber.assertComplete().assertNoErrors(); + }); } @Test public void assertTerminalEventNotReceived() { - PublishProcessor p = PublishProcessor.create(); - TestSubscriber subscriber = new TestSubscriber(); - p.subscribe(subscriber); + assertThrows(AssertionError.class, () -> { + PublishProcessor p = PublishProcessor.create(); + TestSubscriber subscriber = new TestSubscriber<>(); + p.subscribe(subscriber); - p.onNext(1); - p.onNext(2); + p.onNext(1); + p.onNext(2); - thrown.expect(AssertionError.class); - // FIXME different message format -// thrown.expectMessage("No terminal events received."); - - subscriber.assertValues(1, 2); - subscriber.assertValueCount(2); - subscriber.assertComplete().assertNoErrors(); + subscriber.assertValues(1, 2); + subscriber.assertValueCount(2); + subscriber.assertComplete().assertNoErrors(); + }); } @Test @@ -108,7 +109,7 @@ public void wrappingMock() { Subscriber mockSubscriber = TestHelper.mockSubscriber(); - oi.subscribe(new TestSubscriber(mockSubscriber)); + oi.subscribe(new TestSubscriber<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -121,7 +122,7 @@ public void wrappingMock() { public void wrappingMockWhenUnsubscribeInvolved() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)).take(2); Subscriber mockSubscriber = TestHelper.mockSubscriber(); - oi.subscribe(new TestSubscriber(mockSubscriber)); + oi.subscribe(new TestSubscriber<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -132,12 +133,12 @@ public void wrappingMockWhenUnsubscribeInvolved() { @Test public void errorSwallowed() { - Flowable.error(new RuntimeException()).subscribe(new TestSubscriber()); + Flowable.error(new RuntimeException()).subscribe(new TestSubscriber<>()); } @Test public void nullExpected() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onNext(1); try { @@ -151,7 +152,7 @@ public void nullExpected() { @Test public void nullActual() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onNext(null); try { @@ -171,7 +172,7 @@ public void createDelegate() { assertFalse(to.hasSubscription()); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); assertTrue(to.hasSubscription()); @@ -243,7 +244,7 @@ public void assertError() { // expected } - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertSubscribed(); @@ -315,7 +316,7 @@ public void valueAndClass() { public void assertFailure() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onError(new TestException("Forced failure")); @@ -330,18 +331,18 @@ public void assertFailure() { public void assertFuseable() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to = TestObserver.create(); - to.onSubscribe(new ScalarDisposable(to, 1)); + to.onSubscribe(new ScalarDisposable<>(to, 1)); } @Test public void assertResult() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onComplete(); @@ -378,7 +379,7 @@ public void assertResult() { public void await() throws Exception { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); assertFalse(to.await(100, TimeUnit.MILLISECONDS)); @@ -406,7 +407,7 @@ public void await() throws Exception { final TestObserver to1 = TestObserver.create(); - to1.onSubscribe(Disposables.empty()); + to1.onSubscribe(Disposable.empty()); Schedulers.single().scheduleDirect(new Runnable() { @Override @@ -422,7 +423,7 @@ public void run() { public void onNext() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); assertEquals(0, to.values().size()); @@ -445,7 +446,7 @@ public void onNext() { public void multipleTerminals() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertNotComplete(); @@ -479,7 +480,7 @@ public void multipleTerminals() { public void assertValue() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.assertValue(1); @@ -519,7 +520,7 @@ public void onNextMisbehave() { to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onNext(null); @@ -530,7 +531,7 @@ public void onNextMisbehave() { public void awaitTerminalEventInterrupt() { final TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); Thread.currentThread().interrupt(); @@ -559,7 +560,7 @@ public void awaitTerminalEventInterrupt() { public void assertTerminated2() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onError(new TestException()); to.onError(new IOException()); @@ -573,7 +574,7 @@ public void assertTerminated2() { to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onError(new TestException()); to.onComplete(); @@ -589,9 +590,9 @@ public void onSubscribe() { to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); to.onSubscribe(d1); @@ -602,7 +603,7 @@ public void onSubscribe() { to = TestObserver.create(); to.dispose(); - d1 = Disposables.empty(); + d1 = Disposable.empty(); to.onSubscribe(d1); @@ -614,7 +615,7 @@ public void onSubscribe() { public void assertValueSequence() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onNext(1); to.onNext(2); @@ -645,7 +646,7 @@ public void assertValueSequence() { @Test public void assertEmpty() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); try { to.assertEmpty(); @@ -654,7 +655,7 @@ public void assertEmpty() { // expected } - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertEmpty(); @@ -670,7 +671,7 @@ public void assertEmpty() { @Test public void awaitDoneTimed() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Thread.currentThread().interrupt(); @@ -683,7 +684,7 @@ public void awaitDoneTimed() { @Test public void assertErrorMultiple() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); TestException e = new TestException(); to.onError(e); @@ -711,7 +712,7 @@ public void assertErrorMultiple() { @Test public void errorInPredicate() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); to.onError(new RuntimeException()); try { to.assertError(new Predicate() { @@ -729,9 +730,9 @@ public boolean test(Throwable throwable) throws Exception { @Test public void assertComplete() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.assertComplete(); @@ -756,7 +757,7 @@ public void assertComplete() { @Test public void completeWithoutOnSubscribe() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); to.onComplete(); @@ -765,7 +766,7 @@ public void completeWithoutOnSubscribe() { @Test public void completeDelegateThrows() { - TestObserver to = new TestObserver(new Observer() { + TestObserver to = new TestObserver<>(new Observer() { @Override public void onSubscribe(Disposable d) { @@ -789,7 +790,7 @@ public void onComplete() { }); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.onComplete(); @@ -801,7 +802,7 @@ public void onComplete() { @Test public void errorDelegateThrows() { - TestObserver to = new TestObserver(new Observer() { + TestObserver to = new TestObserver<>(new Observer() { @Override public void onSubscribe(Disposable d) { @@ -825,7 +826,7 @@ public void onComplete() { }); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.onError(new IOException()); @@ -853,22 +854,22 @@ public void errorMeansDisposed() { @Test public void assertValuePredicateEmpty() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.empty().subscribe(to); + Observable.empty().subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - to.assertValue(new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + to.assertValue(new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValuePredicateMatch() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1).subscribe(to); @@ -881,52 +882,52 @@ public void assertValuePredicateMatch() { @Test public void assertValuePredicateNoMatch() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("Value 1 (class: Integer) at position 0 did not pass the predicate (latch = 0, values = 1, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.just(1).subscribe(to); + Observable.just(1).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - to.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 1; - } + to.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 1; + } + }); }); } @Test public void assertValuePredicateMatchButMore() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("The first value passed the predicate but this consumer received more than one value (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.just(1, 2).subscribe(to); + Observable.just(1, 2).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value present but other values as well"); - to.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + to.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertValueAtPredicateEmpty() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.empty().subscribe(to); + Observable.empty().subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - to.assertValueAt(0, new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + to.assertValueAt(0, new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValueAtPredicateMatch() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just(1, 2).subscribe(to); @@ -939,48 +940,63 @@ public void assertValueAtPredicateMatch() { @Test public void assertValueAtPredicateNoMatch() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("Value 3 (class: Integer) at position 2 did not pass the predicate (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.just(1, 2, 3).subscribe(to); + Observable.just(1, 2, 3).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - to.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 3; - } + to.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 3; + } + }); }); } @Test public void assertValueAtInvalidIndex() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.just(1, 2).subscribe(to); + Observable.just(1, 2).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)"); - to.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + to.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); + }); + } + + @Test + public void assertValueAtInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just(1, 2).subscribe(to); + + to.assertValueAt(-2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertValueAtIndexEmpty() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.empty().subscribe(to); + Observable.empty().subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - to.assertValueAt(0, "a"); + to.assertValueAt(0, "a"); + }); } @Test public void assertValueAtIndexMatch() { - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); Observable.just("a", "b").subscribe(to); @@ -989,24 +1005,134 @@ public void assertValueAtIndexMatch() { @Test public void assertValueAtIndexNoMatch() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("\nexpected: b (class: String)\ngot: c (class: String); Value at position 2 differ (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValueAt(2, "b"); + }); + } + + @Test + public void assertValueAtIndexThrowsMessageMatchRegex() { + assertThrowsWithMessageMatchRegex(ASSERT_MESSAGE_REGEX, AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValueAt(2, "b"); + }); + } + + @Test + public void assertValuesCountNoMatch() { + assertThrowsWithMessage("\nexpected: 2 [a, b]\ngot: 3 [a, b, c]; Value count differs (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValues("a", "b"); + }); + } + + @Test + public void assertValuesCountThrowsMessageMatchRegex() { + assertThrowsWithMessageMatchRegex(ASSERT_MESSAGE_REGEX, AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.just("a", "b", "c").subscribe(to); + Observable.just("a", "b", "c").subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("expected: b (class: String) but was: c (class: String) (latch = 0, values = 3, errors = 0, completions = 1)"); - to.assertValueAt(2, "b"); + to.assertValues("a", "b"); + }); + } + + @Test + public void assertValuesNoMatch() { + assertThrowsWithMessage("\nexpected: d (class: String)\ngot: c (class: String); Value at position 2 differ (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValues("a", "b", "d"); + }); + } + + @Test + public void assertValuesThrowsMessageMatchRegex() { + assertThrowsWithMessageMatchRegex(ASSERT_MESSAGE_REGEX, AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValues("a", "b", "d"); + }); + } + + @Test + public void assertValueCountNoMatch() { + assertThrowsWithMessage("\nexpected: 2\ngot: 3; Value counts differ (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValueCount(2); + }); + } + + @Test + public void assertValueCountThrowsMessageMatchRegex() { + assertThrowsWithMessageMatchRegex(ASSERT_MESSAGE_REGEX, AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValueCount(2); + }); + } + + @Test + public void assertValueSequenceNoMatch() { + assertThrowsWithMessage("\nexpected: d (class: String)\ngot: c (class: String); Value at position 2 differ (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValueSequence(Arrays.asList("a", "b", "d")); + }); + } + + @Test + public void assertValueSequenceThrowsMessageMatchRegex() { + assertThrowsWithMessageMatchRegex(ASSERT_MESSAGE_REGEX, AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b", "c").subscribe(to); + + to.assertValueSequence(Arrays.asList("a", "b", "d")); + }); } @Test public void assertValueAtIndexInvalidIndex() { - TestObserver to = new TestObserver(); + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); - Observable.just("a", "b").subscribe(to); + Observable.just("a", "b").subscribe(to); + + to.assertValueAt(2, "c"); + }); + } + + @Test + public void assertValueAtIndexInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserver to = new TestObserver<>(); + + Observable.just("a", "b").subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)"); - to.assertValueAt(2, "c"); + to.assertValueAt(-2, "c"); + }); } @Test @@ -1028,7 +1154,7 @@ public void withTag() { @Test public void assertValuesOnly() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertValuesOnly(); to.onNext(5); @@ -1041,7 +1167,7 @@ public void assertValuesOnly() { @Test public void assertValuesOnlyThrowsOnUnexpectedValue() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertValuesOnly(); to.onNext(5); @@ -1060,7 +1186,7 @@ public void assertValuesOnlyThrowsOnUnexpectedValue() { @Test public void assertValuesOnlyThrowsWhenCompleted() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onComplete(); @@ -1075,7 +1201,7 @@ public void assertValuesOnlyThrowsWhenCompleted() { @Test public void assertValuesOnlyThrowsWhenErrored() { TestObserver to = TestObserver.create(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onError(new TestException()); @@ -1086,4 +1212,34 @@ public void assertValuesOnlyThrowsWhenErrored() { // expected } } + + @Test + public void onErrorIsNull() { + TestObserver to = TestObserver.create(); + to.onSubscribe(Disposable.empty()); + + to.onError(null); + + to.assertFailure(NullPointerException.class); + } + + @Test + public void awaitCountTimeout() { + TestObserver to = TestObserver.create(); + to.onSubscribe(Disposable.empty()); + to.awaitCount(1); + assertTrue(to.timeout); + } + + @Test(expected = RuntimeException.class) + public void awaitCountInterrupted() { + try { + TestObserver to = TestObserver.create(); + to.onSubscribe(Disposable.empty()); + Thread.currentThread().interrupt(); + to.awaitCount(1); + } finally { + Thread.interrupted(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/internal/queue/SimpleQueueTest.java b/src/test/java/io/reactivex/rxjava3/operators/SimpleQueueTest.java similarity index 89% rename from src/test/java/io/reactivex/rxjava3/internal/queue/SimpleQueueTest.java rename to src/test/java/io/reactivex/rxjava3/operators/SimpleQueueTest.java index d58918d04d..b9f998c1de 100644 --- a/src/test/java/io/reactivex/rxjava3/internal/queue/SimpleQueueTest.java +++ b/src/test/java/io/reactivex/rxjava3/operators/SimpleQueueTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ * https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/atomic */ -package io.reactivex.rxjava3.internal.queue; +package io.reactivex.rxjava3.operators; import static org.junit.Assert.*; @@ -25,30 +25,31 @@ import org.junit.Test; import io.reactivex.rxjava3.core.RxJavaTest; +import io.reactivex.rxjava3.internal.queue.MpscLinkedQueue; public class SimpleQueueTest extends RxJavaTest { @Test(expected = NullPointerException.class) public void spscArrayQueueNull() { - SpscArrayQueue q = new SpscArrayQueue(16); + SpscArrayQueue q = new SpscArrayQueue<>(16); q.offer(null); } @Test(expected = NullPointerException.class) public void spscLinkedArrayQueueNull() { - SpscLinkedArrayQueue q = new SpscLinkedArrayQueue(16); + SpscLinkedArrayQueue q = new SpscLinkedArrayQueue<>(16); q.offer(null); } @Test(expected = NullPointerException.class) public void mpscLinkedQueueNull() { - MpscLinkedQueue q = new MpscLinkedQueue(); + MpscLinkedQueue q = new MpscLinkedQueue<>(); q.offer(null); } @Test public void spscArrayQueueBiOffer() { - SpscArrayQueue q = new SpscArrayQueue(16); + SpscArrayQueue q = new SpscArrayQueue<>(16); q.offer(1, 2); assertEquals(1, q.poll()); @@ -58,7 +59,7 @@ public void spscArrayQueueBiOffer() { @Test public void spscLinkedArrayQueueBiOffer() { - SpscLinkedArrayQueue q = new SpscLinkedArrayQueue(16); + SpscLinkedArrayQueue q = new SpscLinkedArrayQueue<>(16); q.offer(1, 2); assertEquals(1, q.poll()); @@ -68,7 +69,7 @@ public void spscLinkedArrayQueueBiOffer() { @Test public void mpscLinkedQueueBiOffer() { - MpscLinkedQueue q = new MpscLinkedQueue(); + MpscLinkedQueue q = new MpscLinkedQueue<>(); q.offer(1, 2); assertEquals(1, q.poll()); @@ -78,7 +79,7 @@ public void mpscLinkedQueueBiOffer() { @Test public void spscBiOfferCapacity() { - SpscArrayQueue q = new SpscArrayQueue(8); + SpscArrayQueue q = new SpscArrayQueue<>(8); assertTrue(q.offer(1, 2)); assertTrue(q.offer(3, 4)); assertTrue(q.offer(5, 6)); @@ -90,7 +91,7 @@ public void spscBiOfferCapacity() { @Test public void spscLinkedNewBufferPeek() { - SpscLinkedArrayQueue q = new SpscLinkedArrayQueue(8); + SpscLinkedArrayQueue q = new SpscLinkedArrayQueue<>(8); assertTrue(q.offer(1, 2)); assertTrue(q.offer(3, 4)); assertTrue(q.offer(5, 6)); @@ -107,7 +108,7 @@ public void spscLinkedNewBufferPeek() { @Test public void mpscOfferPollRace() throws Exception { - final MpscLinkedQueue q = new MpscLinkedQueue(); + final MpscLinkedQueue q = new MpscLinkedQueue<>(); final AtomicInteger c = new AtomicInteger(3); @@ -160,7 +161,7 @@ public void run() { @Test public void spscLinkedArrayQueueNoNepotism() { - SpscLinkedArrayQueue q = new SpscLinkedArrayQueue(16); + SpscLinkedArrayQueue q = new SpscLinkedArrayQueue<>(16); AtomicReferenceArray ara = q.producerBuffer; diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java index 044c429d6c..f582966cf9 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelCollectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public void subscriberCount() { .collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -45,7 +45,6 @@ public void accept(List a, Integer b) throws Exception { })); } - @SuppressWarnings("unchecked") @Test public void initialCrash() { Flowable.range(1, 5) @@ -66,7 +65,6 @@ public void accept(List a, Integer b) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void reducerCrash() { Flowable.range(1, 5) @@ -74,7 +72,7 @@ public void reducerCrash() { .collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -99,7 +97,7 @@ public void cancel() { .collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -117,7 +115,6 @@ public void accept(List a, Integer b) throws Exception { assertFalse(pp.hasSubscribers()); } - @SuppressWarnings("unchecked") @Test public void error() { Flowable.error(new TestException()) @@ -125,7 +122,7 @@ public void error() { .collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Integer>() { @Override @@ -138,7 +135,6 @@ public void accept(List a, Integer b) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void doubleError() { List errors = TestHelper.trackPluginErrors(); @@ -147,7 +143,7 @@ public void doubleError() { .collect(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiConsumer, Object>() { @Override @@ -167,4 +163,11 @@ public void accept(List a, Object b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + pf -> pf.collect(ArrayList::new, ArrayList::add) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java index 47371b3073..ba76f69cfb 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelDoOnNextTryTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -189,7 +189,6 @@ public void accept(Integer v) throws Exception { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void doOnNextFailHandlerThrows() { TestSubscriberEx ts = Flowable.range(0, 2) @@ -336,7 +335,6 @@ public void accept(Integer v) throws Exception { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void doOnNextFailHandlerThrowsConditional() { TestSubscriberEx ts = Flowable.range(0, 2) @@ -386,4 +384,23 @@ public void filterInvalidSourceConditional() { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .doOnNext(v -> { }, ParallelFailureHandling.SKIP) + .sequential() + ); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .doOnNext(v -> { }, ParallelFailureHandling.SKIP) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java index 1b77450986..31e9b9a7c4 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -119,4 +119,45 @@ public boolean test(Integer v) throws Exception { .test() .assertFailure(TestException.class); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true) + .sequential() + ); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true) + .filter(v -> true) + .sequential() + ); + } + + @Test + public void conditionalFalseTrue() { + Flowable.just(1) + .parallel() + .filter(v -> false) + .filter(v -> true) + .sequential() + .test() + .assertResult(); + } + + @Test + public void conditionalTrueFalse() { + Flowable.just(1) + .parallel() + .filter(v -> true) + .filter(v -> false) + .sequential() + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java index f2f7377942..6940062989 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFilterTryTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -192,7 +192,6 @@ public boolean test(Integer v) throws Exception { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void filterFailHandlerThrows() { TestSubscriberEx ts = Flowable.range(0, 2) @@ -327,7 +326,6 @@ public boolean test(Integer v) throws Exception { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void filterFailHandlerThrowsConditional() { TestSubscriberEx ts = Flowable.range(0, 2) @@ -375,4 +373,45 @@ public void filterInvalidSourceConditional() { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + ); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .filter(v -> true, ParallelFailureHandling.SKIP) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + ); + } + + @Test + public void conditionalFalseTrue() { + Flowable.just(1) + .parallel() + .filter(v -> false, ParallelFailureHandling.SKIP) + .filter(v -> true, ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertResult(); + } + + @Test + public void conditionalTrueFalse() { + Flowable.just(1) + .parallel() + .filter(v -> true, ParallelFailureHandling.SKIP) + .filter(v -> false, ParallelFailureHandling.SKIP) + .sequential() + .test() + .assertResult(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java new file mode 100644 index 0000000000..8c18de1b0b --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlatMapIterableTest.java @@ -0,0 +1,72 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.parallel; + +import java.util.Arrays; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; + +public class ParallelFlatMapIterableTest extends RxJavaTest { + + @Test + public void subscriberCount() { + ParallelFlowableTest.checkSubscriberCount(Flowable.range(1, 5).parallel() + .flatMapIterable(v -> Arrays.asList(1, 2, 3))); + } + + @Test + public void normal() { + for (int i = 1; i < 32; i++) { + Flowable.range(1, 1000) + .parallel(i) + .flatMapIterable(v -> Arrays.asList(v, v + 1)) + .sequential() + .test() + .withTag("Parallelism: " + i) + .assertValueCount(2000) + .assertNoErrors() + .assertComplete(); + } + } + + @Test + public void none() { + for (int i = 1; i < 32; i++) { + Flowable.range(1, 1000) + .parallel(i) + .flatMapIterable(v -> Arrays.asList()) + .sequential() + .test() + .withTag("Parallelism: " + i) + .assertResult(); + } + } + + @Test + public void mixed() { + for (int i = 1; i < 32; i++) { + Flowable.range(1, 1000) + .parallel(i) + .flatMapIterable(v -> v % 2 == 0 ? Arrays.asList(v) : Arrays.asList()) + .sequential() + .test() + .withTag("Parallelism: " + i) + .assertValueCount(500) + .assertNoErrors() + .assertComplete(); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlowableTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlowableTest.java index 40afd9a511..c97bc5cf56 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlowableTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFlowableTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -49,7 +49,7 @@ public Integer apply(Integer v) throws Exception { .sequential() ; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -77,7 +77,7 @@ public Integer apply(Integer v) throws Exception { .sequential() ; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -113,7 +113,7 @@ public Integer apply(Integer v) throws Exception { .sequential() ; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -154,7 +154,7 @@ public Integer apply(Integer v) throws Exception { .sequential() ; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); result.subscribe(ts); @@ -176,7 +176,7 @@ public Integer apply(Integer v) throws Exception { @Test public void reduceFull() { for (int i = 1; i <= Runtime.getRuntime().availableProcessors() * 2; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 10) .parallel(i) @@ -205,7 +205,7 @@ public void parallelReduceFull() { Scheduler scheduler = Schedulers.from(exec); try { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, n) .map(new Function() { @@ -236,10 +236,9 @@ public Long apply(Long a, Long b) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void toSortedList() { - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.fromArray(10, 9, 8, 7, 6, 5, 4, 3, 2, 1) .parallel() @@ -251,7 +250,7 @@ public void toSortedList() { @Test public void sorted() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.fromArray(10, 9, 8, 7, 6, 5, 4, 3, 2, 1) .parallel() @@ -278,11 +277,11 @@ public void collect() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(1, 10) .parallel() .collect(as, new BiConsumer, Integer>() { @@ -307,10 +306,9 @@ public Iterable apply(List v) throws Exception { TestHelper.assertValueSet(ts, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } - @SuppressWarnings("unchecked") @Test public void from() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ParallelFlowable.fromArray(Flowable.range(1, 5), Flowable.range(6, 5)) .sequential() @@ -325,7 +323,7 @@ public void from() { @Test public void concatMapUnordered() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(1, 5) .parallel() @@ -347,7 +345,7 @@ public Publisher apply(Integer v) throws Exception { @Test public void flatMapUnordered() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.range(1, 5) .parallel() @@ -377,10 +375,10 @@ public void collectAsyncFused() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.range(1, 100000) .parallel(3) @@ -424,10 +422,10 @@ public void collectAsync() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.range(1, 100000).hide() .parallel(3) @@ -471,10 +469,10 @@ public void collectAsync2() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.range(1, 100000).hide() .observeOn(s) @@ -519,10 +517,10 @@ public void collectAsync3() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.range(1, 100000).hide() .observeOn(s) @@ -567,10 +565,10 @@ public void collectAsync3Fused() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.range(1, 100000) .observeOn(s) @@ -615,10 +613,10 @@ public void collectAsync3Take() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); Flowable.range(1, 100000) .take(1000) @@ -664,10 +662,10 @@ public void collectAsync4Take() { Supplier> as = new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }; - TestSubscriber> ts = new TestSubscriber>(); + TestSubscriber> ts = new TestSubscriber<>(); UnicastProcessor up = UnicastProcessor.create(); @@ -711,7 +709,7 @@ public void accept(List v) throws Exception { @Test public void emptySourceZeroRequest() { - TestSubscriber ts = new TestSubscriber(0); + TestSubscriber ts = new TestSubscriber<>(0); Flowable.range(1, 3).parallel(3).sequential().subscribe(ts); @@ -762,7 +760,7 @@ public void parallelismAndPrefetchAsync() { @SuppressWarnings("unchecked") @Test public void badParallelismStage() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.range(1, 10) .parallel(2) @@ -774,9 +772,9 @@ public void badParallelismStage() { @SuppressWarnings("unchecked") @Test public void badParallelismStage2() { - TestSubscriber ts1 = new TestSubscriber(); - TestSubscriber ts2 = new TestSubscriber(); - TestSubscriber ts3 = new TestSubscriber(); + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber<>(); + TestSubscriber ts3 = new TestSubscriber<>(); Flowable.range(1, 10) .parallel(2) @@ -1305,7 +1303,7 @@ public static void checkSubscriberCount(ParallelFlowable source) { TestSubscriber[] consumers = new TestSubscriber[n + 1]; for (int i = 0; i <= n; i++) { - consumers[i] = new TestSubscriber(); + consumers[i] = new TestSubscriber<>(); } source.subscribe(consumers); @@ -1323,7 +1321,7 @@ public void checkAddBiConsumer() { @Test public void mergeBiFunction() throws Exception { - MergerBiFunction f = new MergerBiFunction(Functions.naturalComparator()); + MergerBiFunction f = new MergerBiFunction<>(Functions.naturalComparator()); assertEquals(0, f.apply(Collections.emptyList(), Collections.emptyList()).size()); @@ -1331,12 +1329,12 @@ public void mergeBiFunction() throws Exception { for (int i = 0; i < 4; i++) { int k = 0; - List list1 = new ArrayList(); + List list1 = new ArrayList<>(); for (int j = 0; j < i; j++) { list1.add(k++); } - List list2 = new ArrayList(); + List list2 = new ArrayList<>(); for (int j = i; j < 4; j++) { list2.add(k++); } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java index 0d889d21cb..7d4e684bb6 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelFromPublisherTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,14 +21,16 @@ import org.junit.Test; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscribers.BasicFuseableSubscriber; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; -import io.reactivex.rxjava3.processors.UnicastProcessor; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -48,7 +50,7 @@ protected void subscribeActual(Subscriber s) { .parallel(1, 1) .sequential(1) .test(0) - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); } @Test @@ -71,12 +73,12 @@ static final class StripBoundary extends Flowable implements FlowableTrans @Override public Publisher apply(Flowable upstream) { - return new StripBoundary(upstream); + return new StripBoundary<>(upstream); } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new StripBoundarySubscriber(s)); + source.subscribe(new StripBoundarySubscriber<>(s)); } static final class StripBoundarySubscriber extends BasicFuseableSubscriber { @@ -117,7 +119,7 @@ public Object apply(Integer v) throws Exception { throw new TestException(); } }) - .compose(new StripBoundary(null)) + .compose(new StripBoundary<>(null)) .parallel() .sequential() .test() @@ -137,7 +139,7 @@ public Object apply(Integer v) throws Exception { throw new TestException(); } }) - .compose(new StripBoundary(null)) + .compose(new StripBoundary<>(null)) .parallel() .sequential() .test() @@ -148,8 +150,8 @@ public Object apply(Integer v) throws Exception { @Test public void boundaryConfinement() { - final Set between = new HashSet(); - final ConcurrentHashMap processing = new ConcurrentHashMap(); + final Set between = new HashSet<>(); + final ConcurrentHashMap processing = new ConcurrentHashMap<>(); TestSubscriberEx ts = Flowable.range(1, 10) .observeOn(Schedulers.single(), false, 1) @@ -187,4 +189,107 @@ public Object apply(Integer v) throws Exception { assertTrue(map.toString(), e.contains("RxComputationThreadPool")); } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(PublishProcessor.create().parallel()); + } + + @Test + public void syncFusedEmptyPoll() { + Flowable.just(1, 2) + .filter(v -> v == 1) + .compose(TestHelper.flowableStripBoundary()) + .parallel(1) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void asyncFusedEmptyPoll() { + UnicastProcessor up = UnicastProcessor.create(); + up.onNext(1); + up.onNext(2); + up.onComplete(); + + up + .filter(v -> v == 1) + .compose(TestHelper.flowableStripBoundary()) + .parallel(1) + .sequential() + .test() + .assertResult(1); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> f.parallel().sequential()); + } + + @SuppressWarnings("unchecked") + @Test + public void requestUnboundedRace() { + FlowableSubscriber fs = new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestHelper.race( + () -> s.request(Long.MAX_VALUE), + () -> s.request(Long.MAX_VALUE) + ); + } + } + }; + + PublishProcessor.create() + .parallel(1) + .subscribe(new FlowableSubscriber[] { fs }); + } + + @SuppressWarnings("unchecked") + @Test + public void requestRace() { + FlowableSubscriber fs = new FlowableSubscriber() { + + @Override + public void onNext(@NonNull Integer t) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + + @Override + public void onSubscribe(@NonNull Subscription s) { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + TestHelper.race( + () -> s.request(1), + () -> s.request(1) + ); + } + } + }; + + PublishProcessor.create() + .parallel(1) + .subscribe(new FlowableSubscriber[] { fs }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelInvalid.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelInvalid.java index 39e8ac21cf..6b54ca6847 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelInvalid.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelInvalid.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java index fb50a775f8..8ee41cb973 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelJoinTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,14 +14,17 @@ package io.reactivex.rxjava3.parallel; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.reactivestreams.Subscriber; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.processors.PublishProcessor; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -45,7 +48,7 @@ public int parallelism() { } .sequential(1) .test(0) - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); } @Test @@ -78,7 +81,7 @@ public int parallelism() { .sequential(1) .subscribe(ts); - ts.assertFailure(MissingBackpressureException.class, 1); + ts.assertFailure(QueueOverflowException.class, 1); } @Test @@ -108,7 +111,7 @@ public int parallelism() { .sequentialDelayError(1) .test(0) .requestMore(1) - .assertFailure(MissingBackpressureException.class, 1); + .assertFailure(QueueOverflowException.class, 1); } @Test @@ -145,7 +148,7 @@ public int parallelism() { ts.request(1); - ts.assertFailure(MissingBackpressureException.class, 1, 2); + ts.assertFailure(QueueOverflowException.class, 1, 2); } @Test @@ -326,4 +329,198 @@ public Integer apply(Integer v) throws Exception { .test() .assertFailure(TestException.class, 2, 3, 4); } + + @Test + public void takeUntil() { + Flowable.range(1, 10) + .parallel(1) + .sequential() + .takeUntil(v -> true) + .test(0L) + .requestMore(100) + .assertResult(1); + } + + @Test + public void takeUntilDelayError() { + Flowable.range(1, 10) + .parallel(1) + .sequentialDelayError() + .takeUntil(v -> true) + .test(0L) + .requestMore(100) + .assertResult(1); + } + + @Test + public void oneItemNext() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.parallel(1) + .sequential() + .test(0L); + + pp.onNext(1); + + ts.requestMore(10) + .assertValuesOnly(1); + } + + @Test + public void delayErrorOneItemNext() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = pp.parallel(1) + .sequentialDelayError() + .test(0L); + + pp.onNext(1); + + ts.requestMore(10) + .assertValuesOnly(1); + } + + @Test + public void onNextWhileProcessingSlowPath() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + pp.onNext(2); + } + } + }; + + ParallelFlowable.fromArray(pp) + .sequential() + .subscribeWith(ts); + + pp.onNext(1); + + ts + .assertValuesOnly(1, 2); + } + + @Test + public void delayErrorOnNextWhileProcessingSlowPath() { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + pp.onNext(2); + } + } + }; + + ParallelFlowable.fromArray(pp) + .sequentialDelayError() + .subscribeWith(ts); + + pp.onNext(1); + + ts + .assertValuesOnly(1, 2); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported( + ParallelFlowable.fromArray(PublishProcessor.create()) + .sequential() + ); + } + + @Test + public void onNextMissingBackpressureRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref1.set(s); + } + }; + Flowable f2 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref2.set(s); + } + }; + + ParallelFlowable.fromArray(f1, f2) + .sequential(1) + .test(0) + ; + + TestHelper.race( + () -> { + ref1.get().onNext(1); + ref1.get().onNext(2); + }, + () -> { + ref2.get().onNext(3); + ref2.get().onNext(4); + } + ); + + errors.clear(); + } + }); + } + + @Test + public void onNextMissingBackpressureDelayErrorRace() throws Throwable { + TestHelper.withErrorTracking(errors -> { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + + AtomicReference> ref1 = new AtomicReference<>(); + AtomicReference> ref2 = new AtomicReference<>(); + + Flowable f1 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref1.set(s); + } + }; + Flowable f2 = new Flowable() { + @Override + public void subscribeActual(Subscriber s) { + s.onSubscribe(new BooleanSubscription()); + ref2.set(s); + } + }; + + ParallelFlowable.fromArray(f1, f2) + .sequentialDelayError(1) + .test(0) + ; + + TestHelper.race( + () -> { + ref1.get().onNext(1); + ref1.get().onNext(2); + }, + () -> { + ref2.get().onNext(3); + ref2.get().onNext(4); + } + ); + + errors.clear(); + } + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java index c8e850daf1..493b74d34e 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,11 +19,15 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; +import org.reactivestreams.Subscriber; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.TestHelper; @@ -179,4 +183,45 @@ public Object apply(Integer v) throws Exception { .awaitDone(5, TimeUnit.SECONDS) .assertFailure(TestException.class); } + + @Test + public void invalidSubscriberCount() { + TestHelper.checkInvalidParallelSubscribers( + Flowable.range(1, 10).parallel() + .map(v -> v) + ); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.map(v -> v) + ); + + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.map(v -> v) + .filter(v -> true) + ); + } + + @Test + public void conditionalCancelIgnored() { + Flowable f = new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super @NonNull Integer> s) { + @SuppressWarnings("unchecked") + ConditionalSubscriber subscriber = (ConditionalSubscriber)s; + subscriber.onSubscribe(new BooleanSubscription()); + subscriber.tryOnNext(1); + subscriber.tryOnNext(2); + } + }; + + ParallelFlowable.fromArray(f) + .map(v -> { throw new TestException(); }) + .filter(v -> true) + .sequential() + .test() + .assertFailure(TestException.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTryTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTryTest.java index 2df4dd1462..a6d0de2d92 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTryTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelMapTryTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -13,7 +13,7 @@ package io.reactivex.rxjava3.parallel; -import java.util.List; +import java.util.*; import org.junit.Test; @@ -167,7 +167,6 @@ public Integer apply(Integer v) throws Exception { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void mapFailHandlerThrows() { TestSubscriberEx ts = Flowable.range(0, 2) @@ -302,7 +301,6 @@ public Integer apply(Integer v) throws Exception { .assertResult(1); } - @SuppressWarnings("unchecked") @Test public void mapFailHandlerThrowsConditional() { TestSubscriberEx ts = Flowable.range(0, 2) @@ -355,4 +353,24 @@ public void mapInvalidSourceConditional() { public void failureHandlingEnum() { TestHelper.checkEnum(ParallelFailureHandling.class); } + + @Test + public void invalidSubscriberCount() { + TestHelper.checkInvalidParallelSubscribers( + Flowable.range(1, 10).parallel() + .map(v -> v, ParallelFailureHandling.SKIP) + ); + } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.map(v -> v, ParallelFailureHandling.ERROR) + ); + + TestHelper.checkDoubleOnSubscribeParallel( + p -> p.map(v -> v, ParallelFailureHandling.ERROR) + .filter(v -> true) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java index 85b311847a..93d72ef9f6 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelPeekTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class ParallelPeekTest extends RxJavaTest { @@ -37,6 +37,7 @@ public void subscriberCount() { } @Test + @SuppressUndeliverable public void onSubscribeCrash() { Flowable.range(1, 5) .parallel() @@ -125,6 +126,7 @@ public void run() throws Exception { } @Test + @SuppressUndeliverable public void onCompleteCrash() { Flowable.just(1) .parallel() @@ -194,4 +196,13 @@ public void run() throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeFlowable(f -> + ParallelFlowable.fromArray(f) + .doOnComplete(() -> { }) + .sequential() + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java index 56595583bb..d9a43a247c 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceFullTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,7 +16,7 @@ import static org.junit.Assert.*; import java.io.IOException; -import java.util.List; +import java.util.*; import org.junit.Test; @@ -73,7 +73,6 @@ public Integer apply(Integer a, Integer b) throws Exception { } } - @SuppressWarnings("unchecked") @Test public void error2() { List errors = TestHelper.trackPluginErrors(); @@ -165,4 +164,11 @@ public Integer apply(Integer a, Integer b) throws Exception { .test() .assertFailure(TestException.class); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallelToFlowable( + pf -> pf.reduce((a, b) -> a) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java index 61ef50c1f6..9c32f6afc8 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelReduceTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ public void subscriberCount() { .reduce(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiFunction, Integer, List>() { @Override @@ -46,7 +46,6 @@ public List apply(List a, Integer b) throws Exception { })); } - @SuppressWarnings("unchecked") @Test public void initialCrash() { Flowable.range(1, 5) @@ -68,7 +67,6 @@ public List apply(List a, Integer b) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void reducerCrash() { Flowable.range(1, 5) @@ -76,7 +74,7 @@ public void reducerCrash() { .reduce(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiFunction, Integer, List>() { @Override @@ -102,7 +100,7 @@ public void cancel() { .reduce(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiFunction, Integer, List>() { @Override @@ -121,7 +119,6 @@ public List apply(List a, Integer b) throws Exception { assertFalse(pp.hasSubscribers()); } - @SuppressWarnings("unchecked") @Test public void error() { Flowable.error(new TestException()) @@ -129,7 +126,7 @@ public void error() { .reduce(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiFunction, Integer, List>() { @Override @@ -143,7 +140,6 @@ public List apply(List a, Integer b) throws Exception { .assertFailure(TestException.class); } - @SuppressWarnings("unchecked") @Test public void doubleError() { List errors = TestHelper.trackPluginErrors(); @@ -152,7 +148,7 @@ public void doubleError() { .reduce(new Supplier>() { @Override public List get() throws Exception { - return new ArrayList(); + return new ArrayList<>(); } }, new BiFunction, Object, List>() { @Override @@ -173,4 +169,11 @@ public List apply(List a, Object b) throws Exception { RxJavaPlugins.reset(); } } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel( + pf -> pf.reduce(ArrayList::new, (a, b) -> a) + ); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java index 4b19afdc66..44a860263a 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelRunOnTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -98,7 +98,7 @@ public void subscribe(Subscriber[] subscribers) { .runOn(ImmediateThinScheduler.INSTANCE, 1) .sequential(1) .test(0) - .assertFailure(MissingBackpressureException.class); + .assertFailure(QueueOverflowException.class); } @Test @@ -135,7 +135,7 @@ public void errorConditional() { @SuppressWarnings("unchecked") @Test public void errorConditionalBackpressured() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.error(new TestException()) .parallel(1) @@ -150,7 +150,7 @@ public void errorConditionalBackpressured() { @SuppressWarnings("unchecked") @Test public void emptyConditionalBackpressured() { - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); Flowable.empty() .parallel(1) @@ -322,4 +322,60 @@ public void onNext(Integer t) { ts.assertResult(1); } + + @Test + public void doubleOnSubscribe() { + TestHelper.checkDoubleOnSubscribeParallel(pf -> pf.runOn(ImmediateThinScheduler.INSTANCE)); + } + + @Test + public void doubleOnSubscribeConditional() { + TestHelper.checkDoubleOnSubscribeParallel(pf -> + pf.runOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + ); + } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported( + ParallelFlowable.fromArray(PublishProcessor.create()) + .runOn(ImmediateThinScheduler.INSTANCE) + ); + } + + @SuppressWarnings("unchecked") + @Test + public void asManyItemsAsRequested() { + TestSubscriber ts = new TestSubscriber<>(0); + + Flowable.range(1, 5) + .parallel(1) + .runOn(ImmediateThinScheduler.INSTANCE) + .subscribe(new Subscriber[] { + ts + }); + + ts + .requestMore(5) + .assertResult(1, 2, 3, 4, 5); + } + + @SuppressWarnings("unchecked") + @Test + public void asManyItemsAsRequestedConditional() { + TestSubscriber ts = new TestSubscriber<>(0); + + Flowable.range(1, 5) + .parallel(1) + .runOn(ImmediateThinScheduler.INSTANCE) + .filter(v -> true) + .subscribe(new Subscriber[] { + ts + }); + + ts + .requestMore(5) + .assertResult(1, 2, 3, 4, 5); + } } diff --git a/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java b/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java index d2cc8aaec0..9ba23c30c2 100644 --- a/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java +++ b/src/test/java/io/reactivex/rxjava3/parallel/ParallelSortedJoinTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,6 +24,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.functions.Functions; +import io.reactivex.rxjava3.internal.operators.parallel.ParallelSortedJoin; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -82,7 +83,6 @@ public void error3() { } } - @SuppressWarnings("unchecked") @Test public void error2() { List errors = TestHelper.trackPluginErrors(); @@ -208,4 +208,32 @@ public void run() { TestHelper.race(r1, r2); } } + + @Test + public void badRequest() { + TestHelper.assertBadRequestReported(PublishProcessor.create().parallel().sorted(Functions.naturalComparator())); + } + + @Test + public void comparatorCrashWhileMainOnError() throws Throwable { + TestHelper.withErrorTracking(errors -> { + PublishProcessor> pp1 = PublishProcessor.create(); + PublishProcessor> pp2 = PublishProcessor.create(); + + new ParallelSortedJoin<>(ParallelFlowable.fromArray(pp1, pp2) + , (a, b) -> { + pp1.onError(new IOException()); + throw new TestException(); + }) + .test(); + + pp1.onNext(Arrays.asList(1)); + pp2.onNext(Arrays.asList(2)); + + pp1.onComplete(); + pp2.onComplete(); + + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); + } } diff --git a/src/test/java/io/reactivex/rxjava3/plugins/RxJavaPluginsTest.java b/src/test/java/io/reactivex/rxjava3/plugins/RxJavaPluginsTest.java index 5e0b5ab207..6aa166d118 100644 --- a/src/test/java/io/reactivex/rxjava3/plugins/RxJavaPluginsTest.java +++ b/src/test/java/io/reactivex/rxjava3/plugins/RxJavaPluginsTest.java @@ -1,17 +1,14 @@ -/** +/* * 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. */ package io.reactivex.rxjava3.plugins; @@ -592,6 +589,61 @@ public void onComplete() { .assertComplete(); } + @SuppressWarnings("rawtypes") + @Test + public void parallelFlowableStart() { + try { + RxJavaPlugins.setOnParallelSubscribe(new BiFunction() { + @Override + public Subscriber[] apply(ParallelFlowable f, final Subscriber[] t) { + return new Subscriber[] { new Subscriber() { + + @Override + public void onSubscribe(Subscription s) { + t[0].onSubscribe(s); + } + + @SuppressWarnings("unchecked") + @Override + public void onNext(Object value) { + t[0].onNext((Integer)value - 9); + } + + @Override + public void onError(Throwable e) { + t[0].onError(e); + } + + @Override + public void onComplete() { + t[0].onComplete(); + } + + } + }; + } + }); + + Flowable.range(10, 3) + .parallel(1) + .sequential() + .test() + .assertValues(1, 2, 3) + .assertNoErrors() + .assertComplete(); + } finally { + RxJavaPlugins.reset(); + } + // make sure the reset worked + Flowable.range(10, 3) + .parallel(1) + .sequential() + .test() + .assertValues(10, 11, 12) + .assertNoErrors() + .assertComplete(); + } + @SuppressWarnings("rawtypes") @Test public void singleCreate() { @@ -599,7 +651,7 @@ public void singleCreate() { RxJavaPlugins.setOnSingleAssembly(new Function() { @Override public Single apply(Single t) { - return new SingleJust(10); + return new SingleJust<>(10); } }); @@ -806,7 +858,7 @@ public void onScheduleNewThread() throws InterruptedException { @Test public void onError() { try { - final List list = new ArrayList(); + final List list = new ArrayList<>(); RxJavaPlugins.setErrorHandler(new Consumer() { @Override @@ -827,7 +879,7 @@ public void accept(Throwable t) { @Test public void onErrorNoHandler() { try { - final List list = new ArrayList(); + final List list = new ArrayList<>(); RxJavaPlugins.setErrorHandler(null); @@ -858,7 +910,7 @@ public void uncaughtException(Thread t, Throwable e) { @Test public void onErrorCrashes() { try { - final List list = new ArrayList(); + final List list = new ArrayList<>(); RxJavaPlugins.setErrorHandler(new Consumer() { @Override @@ -893,7 +945,7 @@ public void uncaughtException(Thread t, Throwable e) { @Test public void onErrorWithNull() { try { - final List list = new ArrayList(); + final List list = new ArrayList<>(); RxJavaPlugins.setErrorHandler(new Consumer() { @Override @@ -1176,6 +1228,7 @@ public void onComplete() { } AllSubscriber all = new AllSubscriber(); + Subscriber[] allArray = { all }; assertNull(RxJavaPlugins.onSubscribe(Observable.never(), null)); @@ -1197,6 +1250,10 @@ public void onComplete() { assertSame(all, RxJavaPlugins.onSubscribe(Maybe.never(), all)); + assertNull(RxJavaPlugins.onSubscribe(Flowable.never().parallel(), null)); + + assertSame(allArray, RxJavaPlugins.onSubscribe(Flowable.never().parallel(), allArray)); + final Scheduler s = ImmediateThinScheduler.INSTANCE; Supplier c = new Supplier() { @Override @@ -1261,7 +1318,7 @@ public void reset() { @SuppressWarnings("unchecked") @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onNext(10); observer.onComplete(); } @@ -1523,7 +1580,7 @@ public void onComplete() { @Test public void onErrorNull() { try { - final AtomicReference t = new AtomicReference(); + final AtomicReference t = new AtomicReference<>(); RxJavaPlugins.setErrorHandler(new Consumer() { @Override @@ -1547,7 +1604,7 @@ private static void verifyThread(Scheduler scheduler, String expectedThreadName) assertNotNull(scheduler); Worker w = scheduler.createWorker(); try { - final AtomicReference value = new AtomicReference(); + final AtomicReference value = new AtomicReference<>(); final CountDownLatch cdl = new CountDownLatch(1); w.schedule(new Runnable() { @@ -1703,7 +1760,7 @@ public void onParallelAssembly() { RxJavaPlugins.setOnParallelAssembly(new Function() { @Override public ParallelFlowable apply(ParallelFlowable pf) throws Exception { - return new ParallelFromPublisher(Flowable.just(2), 2, 2); + return new ParallelFromPublisher<>(Flowable.just(2), 2, 2); } }); diff --git a/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java index 421aee51a4..81afc972dc 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/AsyncProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,8 +26,8 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.subscribers.TestSubscriber; import io.reactivex.rxjava3.testsupport.*; @@ -112,6 +112,7 @@ public void subscribeAfterError() { } @Test + @SuppressUndeliverable public void error() { AsyncProcessor processor = AsyncProcessor.create(); @@ -136,7 +137,7 @@ public void unsubscribeBeforeCompleted() { AsyncProcessor processor = AsyncProcessor.create(); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); processor.subscribe(ts); processor.onNext("one"); @@ -185,7 +186,7 @@ public void subscribeCompletionRaceCondition() { */ for (int i = 0; i < 50; i++) { final AsyncProcessor processor = AsyncProcessor.create(); - final AtomicReference value1 = new AtomicReference(); + final AtomicReference value1 = new AtomicReference<>(); processor.subscribe(new Consumer() { @@ -243,7 +244,7 @@ public void run() { private static class SubjectSubscriberThread extends Thread { private final AsyncProcessor processor; - private final AtomicReference value = new AtomicReference(); + private final AtomicReference value = new AtomicReference<>(); SubjectSubscriberThread(AsyncProcessor processor) { this.processor = processor; @@ -327,7 +328,7 @@ public void currentStateMethodsError() { @Test public void fusionLive() { - AsyncProcessor ap = new AsyncProcessor(); + AsyncProcessor ap = new AsyncProcessor<>(); TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); @@ -350,7 +351,7 @@ public void fusionLive() { @Test public void fusionOfflie() { - AsyncProcessor ap = new AsyncProcessor(); + AsyncProcessor ap = new AsyncProcessor<>(); ap.onNext(1); ap.onComplete(); @@ -424,6 +425,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -461,7 +463,7 @@ public void run() { public void onNextCrossCancel() { AsyncProcessor p = AsyncProcessor.create(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); TestSubscriber ts1 = new TestSubscriber() { @Override public void onNext(Object t) { @@ -481,10 +483,11 @@ public void onNext(Object t) { } @Test + @SuppressUndeliverable public void onErrorCrossCancel() { AsyncProcessor p = AsyncProcessor.create(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); TestSubscriber ts1 = new TestSubscriber() { @Override public void onError(Throwable t) { @@ -506,7 +509,7 @@ public void onError(Throwable t) { public void onCompleteCrossCancel() { AsyncProcessor p = AsyncProcessor.create(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); TestSubscriber ts1 = new TestSubscriber() { @Override public void onComplete() { @@ -523,4 +526,9 @@ public void onComplete() { ts1.assertResult(); ts2.assertEmpty(); } + + @Test + public void cancel() { + TestHelper.checkDisposed(AsyncProcessor.create()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java index 0d61998b30..991a599b82 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/BehaviorProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -137,7 +137,7 @@ public void completedStopsEmittingData() { Subscriber observerB = TestHelper.mockSubscriber(); Subscriber observerC = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(observerA); + TestSubscriber ts = new TestSubscriber<>(observerA); channel.subscribe(ts); channel.subscribe(observerB); @@ -389,7 +389,7 @@ public void run() { } }); - final AtomicReference o = new AtomicReference(); + final AtomicReference o = new AtomicReference<>(); rs.subscribeOn(s).observeOn(Schedulers.io()) .subscribe(new DefaultSubscriber() { @@ -612,6 +612,36 @@ public void run() { } } + @Test + public void multipleSubscribersRemoveSomeRace() { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + final BehaviorProcessor p = BehaviorProcessor.create(); + + final TestSubscriber ts1 = p.test(); + final TestSubscriber ts2 = p.test(); + final TestSubscriber ts3 = p.test(); + + Runnable r1 = new Runnable() { + @Override + public void run() { + ts1.cancel(); + } + }; + + Runnable r2 = new Runnable() { + @Override + public void run() { + ts2.cancel(); + } + }; + + TestHelper.race(r1, r2); + + p.onNext(1); + ts3.assertValuesOnly(1); + } + } + @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void subscribeOnNextRace() { @@ -673,12 +703,14 @@ public void offer() { ts = pp.test(1); - assertTrue(pp.offer(null)); - - ts.assertFailure(NullPointerException.class, 2); + try { + pp.offer(null); + fail("Should have thrown NPE!"); + } catch (NullPointerException expected) { + // expected + } - assertTrue(pp.hasThrowable()); - assertTrue(pp.getThrowable().toString(), pp.getThrowable() instanceof NullPointerException); + ts.assertValuesOnly(2); } @Test @@ -715,7 +747,7 @@ public void completeSubscribeRace() throws Exception { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final BehaviorProcessor p = BehaviorProcessor.create(); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -742,7 +774,7 @@ public void errorSubscribeRace() throws Exception { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final BehaviorProcessor p = BehaviorProcessor.create(); - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final TestException ex = new TestException(); @@ -804,9 +836,9 @@ public void behaviorDisposableDisposeState() { BehaviorProcessor bp = BehaviorProcessor.create(); bp.onNext(1); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - BehaviorSubscription bs = new BehaviorSubscription(ts, bp); + BehaviorSubscription bs = new BehaviorSubscription<>(ts, bp); ts.onSubscribe(bs); assertFalse(bs.cancelled); @@ -835,9 +867,9 @@ public void emitFirstDisposeRace() { BehaviorProcessor bp = BehaviorProcessor.create(); bp.onNext(1); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final BehaviorSubscription bs = new BehaviorSubscription(ts, bp); + final BehaviorSubscription bs = new BehaviorSubscription<>(ts, bp); ts.onSubscribe(bs); Runnable r1 = new Runnable() { @@ -864,9 +896,9 @@ public void emitNextDisposeRace() { BehaviorProcessor bp = BehaviorProcessor.create(); bp.onNext(1); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final BehaviorSubscription bs = new BehaviorSubscription(ts, bp); + final BehaviorSubscription bs = new BehaviorSubscription<>(ts, bp); ts.onSubscribe(bs); Runnable r1 = new Runnable() { @@ -891,9 +923,9 @@ public void emittingEmitNext() { BehaviorProcessor bp = BehaviorProcessor.create(); bp.onNext(1); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final BehaviorSubscription bs = new BehaviorSubscription(ts, bp); + final BehaviorSubscription bs = new BehaviorSubscription<>(ts, bp); ts.onSubscribe(bs); bs.emitting = true; @@ -910,9 +942,9 @@ public void badRequest() { BehaviorProcessor bp = BehaviorProcessor.create(); bp.onNext(1); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final BehaviorSubscription bs = new BehaviorSubscription(ts, bp); + final BehaviorSubscription bs = new BehaviorSubscription<>(ts, bp); ts.onSubscribe(bs); bs.request(-1); diff --git a/src/test/java/io/reactivex/rxjava3/processors/FlowableProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/FlowableProcessorTest.java index ae659623e0..1575a5669c 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/FlowableProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/FlowableProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/processors/MulticastProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/MulticastProcessorTest.java index d34fbabcb4..0ac22cf1b2 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/MulticastProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/MulticastProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -229,7 +229,7 @@ public void takeRefCountExact() { @Test public void crossCancel() { - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override @@ -257,7 +257,7 @@ public void onNext(Integer t) { @Test public void crossCancelError() { - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override @@ -285,7 +285,7 @@ public void onError(Throwable t) { @Test public void crossCancelComplete() { - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override @@ -314,7 +314,7 @@ public void onComplete() { @Test public void crossCancel1() { - final TestSubscriber ts1 = new TestSubscriber(1); + final TestSubscriber ts1 = new TestSubscriber<>(1); TestSubscriber ts2 = new TestSubscriber(1) { @Override @@ -466,7 +466,12 @@ public void asyncFused() { up.onNext(i); } - assertFalse(mp.offer(10)); + try { + mp.offer(10); + fail("Should have thrown IllegalStateException"); + } catch (IllegalStateException expected) { + // expected + } up.onComplete(); @@ -536,7 +541,7 @@ public void addRemoveRaceNoRefCount() { final MulticastProcessor mp = MulticastProcessor.create(); final TestSubscriber ts = mp.test(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -565,7 +570,7 @@ public void addRemoveRaceNoRefCountNonEmpty() { mp.test(); final TestSubscriber ts = mp.test(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override @@ -593,7 +598,7 @@ public void addRemoveRaceWitRefCount() { final MulticastProcessor mp = MulticastProcessor.create(true); final TestSubscriber ts = mp.test(); - final TestSubscriber ts2 = new TestSubscriber(); + final TestSubscriber ts2 = new TestSubscriber<>(); Runnable r1 = new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java index 3398e1bc58..380f644518 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/PublishProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,7 +30,7 @@ import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class PublishProcessorTest extends FlowableProcessorTest { @@ -40,6 +40,7 @@ protected FlowableProcessor create() { } @Test + @SuppressUndeliverable public void completed() { PublishProcessor processor = PublishProcessor.create(); @@ -69,7 +70,7 @@ public void completedStopsEmittingData() { Subscriber observerB = TestHelper.mockSubscriber(); Subscriber observerC = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(observerA); + TestSubscriber ts = new TestSubscriber<>(observerA); channel.subscribe(ts); channel.subscribe(observerB); @@ -113,6 +114,7 @@ private void assertCompletedSubscriber(Subscriber subscriber) { } @Test + @SuppressUndeliverable public void error() { PublishProcessor processor = PublishProcessor.create(); @@ -178,7 +180,7 @@ public void unsubscribeFirstSubscriber() { PublishProcessor processor = PublishProcessor.create(); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); processor.subscribe(ts); processor.onNext("one"); @@ -213,7 +215,7 @@ public void nestedSubscribe() { final AtomicInteger countChildren = new AtomicInteger(); final AtomicInteger countTotal = new AtomicInteger(); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); s.flatMap(new Function>() { @@ -264,7 +266,7 @@ public void reSubscribe() { final PublishProcessor pp = PublishProcessor.create(); Subscriber subscriber1 = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber1); + TestSubscriber ts = new TestSubscriber<>(subscriber1); pp.subscribe(ts); // emit @@ -282,7 +284,7 @@ public void reSubscribe() { pp.onNext(2); Subscriber subscriber2 = TestHelper.mockSubscriber(); - TestSubscriber ts2 = new TestSubscriber(subscriber2); + TestSubscriber ts2 = new TestSubscriber<>(subscriber2); pp.subscribe(ts2); // emit @@ -412,7 +414,7 @@ public void requestValidation() { @Test public void crossCancel() { - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override public void onNext(Integer t) { @@ -434,8 +436,9 @@ public void onNext(Integer t) { } @Test + @SuppressUndeliverable public void crossCancelOnError() { - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override public void onError(Throwable t) { @@ -458,7 +461,7 @@ public void onError(Throwable t) { @Test public void crossCancelOnComplete() { - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override public void onComplete() { @@ -597,12 +600,14 @@ public void offer() { ts = pp.test(0); - assertTrue(pp.offer(null)); - - ts.assertFailure(NullPointerException.class); + try { + pp.offer(null); + fail("Should have thrown NPE!"); + } catch (NullPointerException expected) { + // expected + } - assertTrue(pp.hasThrowable()); - assertTrue(pp.getThrowable().toString(), pp.getThrowable() instanceof NullPointerException); + ts.assertEmpty(); } @Test diff --git a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorBoundedConcurrencyTest.java b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorBoundedConcurrencyTest.java index 8f733ae6ed..f080ce5e1d 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorBoundedConcurrencyTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorBoundedConcurrencyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -162,8 +162,8 @@ public void subscribe(Subscriber subscriber) { }); // used to collect results of each thread - final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList>()); - final List threads = Collections.synchronizedList(new ArrayList()); + final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList<>()); + final List threads = Collections.synchronizedList(new ArrayList<>()); for (int i = 1; i <= 200; i++) { final int count = i; @@ -196,7 +196,7 @@ public void run() { } // assert all threads got the same results - List sums = new ArrayList(); + List sums = new ArrayList<>(); for (List values : listOfListsOfValues) { long v = 0; for (long l : values) { @@ -229,7 +229,7 @@ public void run() { public void subscribeCompletionRaceCondition() { for (int i = 0; i < 50; i++) { final ReplayProcessor processor = ReplayProcessor.createUnbounded(); - final AtomicReference value1 = new AtomicReference(); + final AtomicReference value1 = new AtomicReference<>(); processor.subscribe(new Consumer() { @@ -292,7 +292,7 @@ public void run() { public void raceForTerminalState() { final List expected = Arrays.asList(1); for (int i = 0; i < 100000; i++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(1).subscribeOn(Schedulers.computation()).cache().subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertValueSequence(expected); @@ -303,7 +303,7 @@ public void raceForTerminalState() { private static class SubjectObserverThread extends Thread { private final ReplayProcessor processor; - private final AtomicReference value = new AtomicReference(); + private final AtomicReference value = new AtomicReference<>(); SubjectObserverThread(ReplayProcessor processor) { this.processor = processor; @@ -350,7 +350,7 @@ public void run() { } }); - final AtomicReference o = new AtomicReference(); + final AtomicReference o = new AtomicReference<>(); rs // .doOnSubscribe(v -> System.out.println("!! " + j)) diff --git a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorConcurrencyTest.java b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorConcurrencyTest.java index f9754a49ee..3afe66dd25 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorConcurrencyTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorConcurrencyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -162,8 +162,8 @@ public void subscribe(Subscriber subscriber) { }); // used to collect results of each thread - final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList>()); - final List threads = Collections.synchronizedList(new ArrayList()); + final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList<>()); + final List threads = Collections.synchronizedList(new ArrayList<>()); for (int i = 1; i <= 200; i++) { final int count = i; @@ -196,7 +196,7 @@ public void run() { } // assert all threads got the same results - List sums = new ArrayList(); + List sums = new ArrayList<>(); for (List values : listOfListsOfValues) { long v = 0; for (long l : values) { @@ -229,7 +229,7 @@ public void run() { public void subscribeCompletionRaceCondition() { for (int i = 0; i < 50; i++) { final ReplayProcessor processor = ReplayProcessor.create(); - final AtomicReference value1 = new AtomicReference(); + final AtomicReference value1 = new AtomicReference<>(); processor.subscribe(new Consumer() { @@ -292,7 +292,7 @@ public void run() { public void raceForTerminalState() { final List expected = Arrays.asList(1); for (int i = 0; i < 100000; i++) { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(1).subscribeOn(Schedulers.computation()).cache().subscribe(ts); ts.awaitDone(5, TimeUnit.SECONDS); ts.assertValueSequence(expected); @@ -303,7 +303,7 @@ public void raceForTerminalState() { static class SubjectObserverThread extends Thread { private final ReplayProcessor processor; - private final AtomicReference value = new AtomicReference(); + private final AtomicReference value = new AtomicReference<>(); SubjectObserverThread(ReplayProcessor processor) { this.processor = processor; @@ -347,7 +347,7 @@ public void run() { } }); - final AtomicReference o = new AtomicReference(); + final AtomicReference o = new AtomicReference<>(); rs.subscribeOn(s).observeOn(Schedulers.io()) .subscribe(new DefaultSubscriber() { diff --git a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java index 80fe3af7b7..35a877911b 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/ReplayProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,6 +26,7 @@ import org.mockito.*; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; @@ -46,6 +47,7 @@ protected FlowableProcessor create() { } @Test + @SuppressUndeliverable public void completed() { ReplayProcessor processor = ReplayProcessor.create(); @@ -70,13 +72,14 @@ public void completed() { } @Test + @SuppressUndeliverable public void completedStopsEmittingData() { ReplayProcessor channel = ReplayProcessor.create(); Subscriber observerA = TestHelper.mockSubscriber(); Subscriber observerB = TestHelper.mockSubscriber(); Subscriber observerC = TestHelper.mockSubscriber(); Subscriber observerD = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(observerA); + TestSubscriber ts = new TestSubscriber<>(observerA); channel.subscribe(ts); channel.subscribe(observerB); @@ -139,6 +142,7 @@ public void completedStopsEmittingData() { } @Test + @SuppressUndeliverable public void completedAfterError() { ReplayProcessor processor = ReplayProcessor.create(); @@ -169,6 +173,7 @@ private void assertCompletedSubscriber(Subscriber subscriber) { } @Test + @SuppressUndeliverable public void error() { ReplayProcessor processor = ReplayProcessor.create(); @@ -227,7 +232,7 @@ public void unsubscribeFirstSubscriber() { ReplayProcessor processor = ReplayProcessor.create(); Subscriber subscriber = TestHelper.mockSubscriber(); - TestSubscriber ts = new TestSubscriber(subscriber); + TestSubscriber ts = new TestSubscriber<>(subscriber); processor.subscribe(ts); processor.onNext("one"); @@ -258,7 +263,7 @@ private void assertObservedUntilTwo(Subscriber subscriber) { @Test public void newSubscriberDoesntBlockExisting() throws InterruptedException { - final AtomicReference lastValueForSubscriber1 = new AtomicReference(); + final AtomicReference lastValueForSubscriber1 = new AtomicReference<>(); Subscriber subscriber1 = new DefaultSubscriber() { @Override @@ -279,7 +284,7 @@ public void onNext(String v) { }; - final AtomicReference lastValueForSubscriber2 = new AtomicReference(); + final AtomicReference lastValueForSubscriber2 = new AtomicReference<>(); final CountDownLatch oneReceived = new CountDownLatch(1); final CountDownLatch makeSlow = new CountDownLatch(1); final CountDownLatch completed = new CountDownLatch(1); @@ -806,7 +811,7 @@ public void backpressureHonored() { rs.onNext(3); rs.onComplete(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); rs.subscribe(ts); @@ -834,7 +839,7 @@ public void backpressureHonoredSizeBound() { rs.onNext(3); rs.onComplete(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); rs.subscribe(ts); @@ -862,7 +867,7 @@ public void backpressureHonoredTimeBound() { rs.onNext(3); rs.onComplete(); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); rs.subscribe(ts); @@ -1026,7 +1031,7 @@ public void capacityHint() { @Test public void subscribeCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestSubscriber ts = new TestSubscriber(); + final TestSubscriber ts = new TestSubscriber<>(); final ReplayProcessor rp = ReplayProcessor.create(); @@ -1199,6 +1204,30 @@ public void takeSizeAndTime() { .assertResult(2); } + @Test + public void takeSizeAndTime2() { + TestScheduler scheduler = new TestScheduler(); + + ReplayProcessor rp = ReplayProcessor.createWithTimeAndSize(1, TimeUnit.SECONDS, scheduler, 2); + + rp.onNext(1); + rp.onNext(2); + rp.onNext(3); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + cancel(); + onComplete(); + } + }; + + rp + .subscribeWith(ts) + .assertResult(2); + } + @Test public void takeSize() { ReplayProcessor rp = ReplayProcessor.createWithSize(2); @@ -1213,6 +1242,28 @@ public void takeSize() { .assertResult(2); } + @Test + public void takeSize2() { + ReplayProcessor rp = ReplayProcessor.createWithSize(2); + + rp.onNext(1); + rp.onNext(2); + rp.onNext(3); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + cancel(); + onComplete(); + } + }; + + rp + .subscribeWith(ts) + .assertResult(2); + } + @Test public void reentrantDrain() { TestScheduler scheduler = new TestScheduler(); @@ -1781,4 +1832,69 @@ public void timeAndSizeRemoveCorrectNumberOfOld() { rp.test().assertValuesOnly(4, 5); } -} + + @Test + public void terminationSubscriptionRaceUnbounded() throws Throwable { + for (int i = 1; i <= 10000; i++) { + ReplayProcessor source = ReplayProcessor.create(); + PublishProcessor sink = PublishProcessor.create(); + TestSubscriber subscriber = sink.test(); + Schedulers.computation().scheduleDirect(() -> { + // issue signals to the source in adherence to the reactive streams specification + source.onSubscribe(new BooleanSubscription()); + source.onNext("hello"); + source.onNext("world"); + source.onComplete(); + }); + Schedulers.computation().scheduleDirect(() -> { + // connect the source to the sink in parallel with the signals issued to the source + // note the cast() operator, which is here to detect non-String escapees + source.cast(String.class).subscribe(sink); + }); + subscriber.await().assertValues("hello", "world").assertComplete(); + } + } + + @Test + public void terminationSubscriptionRaceSizeBound() throws Throwable { + for (int i = 1; i <= 10000; i++) { + ReplayProcessor source = ReplayProcessor.createWithSize(20); + PublishProcessor sink = PublishProcessor.create(); + TestSubscriber subscriber = sink.test(); + Schedulers.computation().scheduleDirect(() -> { + // issue signals to the source in adherence to the reactive streams specification + source.onSubscribe(new BooleanSubscription()); + source.onNext("hello"); + source.onNext("world"); + source.onComplete(); + }); + Schedulers.computation().scheduleDirect(() -> { + // connect the source to the sink in parallel with the signals issued to the source + // note the cast() operator, which is here to detect non-String escapees + source.cast(String.class).subscribe(sink); + }); + subscriber.await().assertValues("hello", "world").assertComplete(); + } + } + + @Test + public void terminationSubscriptionRaceTimeBound() throws Throwable { + for (int i = 1; i <= 10000; i++) { + ReplayProcessor source = ReplayProcessor.createWithTime(20, TimeUnit.MINUTES, Schedulers.computation()); + PublishProcessor sink = PublishProcessor.create(); + TestSubscriber subscriber = sink.test(); + Schedulers.computation().scheduleDirect(() -> { + // issue signals to the source in adherence to the reactive streams specification + source.onSubscribe(new BooleanSubscription()); + source.onNext("hello"); + source.onNext("world"); + source.onComplete(); + }); + Schedulers.computation().scheduleDirect(() -> { + // connect the source to the sink in parallel with the signals issued to the source + // note the cast() operator, which is here to detect non-String escapees + source.cast(String.class).subscribe(sink); + }); + subscriber.await().assertValues("hello", "world").assertComplete(); + } + }} diff --git a/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java index 0582689830..515b8d1430 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/SerializedProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,6 +20,7 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; @@ -31,8 +32,8 @@ public class SerializedProcessorTest extends RxJavaTest { @Test public void basic() { - SerializedProcessor processor = new SerializedProcessor(PublishProcessor. create()); - TestSubscriber ts = new TestSubscriber(); + SerializedProcessor processor = new SerializedProcessor<>(PublishProcessor.create()); + TestSubscriber ts = new TestSubscriber<>(); processor.subscribe(ts); processor.onNext("hello"); processor.onComplete(); @@ -416,7 +417,7 @@ public void normal() { @Test public void onNextOnNextRace() { - Set expectedSet = new HashSet(Arrays.asList(1, 2)); + Set expectedSet = new HashSet<>(Arrays.asList(1, 2)); for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final FlowableProcessor s = PublishProcessor.create().toSerialized(); @@ -445,7 +446,7 @@ public void run() { .assertValueCount(2) ; - Set actualSet = new HashSet(ts.values()); + Set actualSet = new HashSet<>(ts.values()); assertEquals("" + actualSet, expectedSet, actualSet); } } @@ -664,4 +665,27 @@ public void run() { ts.assertEmpty(); } } + + @Test + public void onErrorQueued() { + FlowableProcessor sp = PublishProcessor.create().toSerialized(); + + TestSubscriber ts = new TestSubscriber() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + sp.onNext(2); + sp.onSubscribe(new BooleanSubscription()); + sp.onError(new TestException()); + } + } + }; + + sp.subscribe(ts); + + sp.onNext(1); + + ts.assertFailure(TestException.class, 1); // errors skip ahead + } } diff --git a/src/test/java/io/reactivex/rxjava3/processors/UnicastProcessorTest.java b/src/test/java/io/reactivex/rxjava3/processors/UnicastProcessorTest.java index a552783994..f65cc46a5e 100644 --- a/src/test/java/io/reactivex/rxjava3/processors/UnicastProcessorTest.java +++ b/src/test/java/io/reactivex/rxjava3/processors/UnicastProcessorTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,9 +25,9 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subscribers.TestSubscriber; @@ -131,14 +131,14 @@ public void run() { public void onTerminateCalledWhenOnError() { final AtomicBoolean didRunOnTerminate = new AtomicBoolean(); - UnicastProcessor us = UnicastProcessor.create(Observable.bufferSize(), new Runnable() { + UnicastProcessor up = UnicastProcessor.create(Observable.bufferSize(), new Runnable() { @Override public void run() { didRunOnTerminate.set(true); } }); assertFalse(didRunOnTerminate.get()); - us.onError(new RuntimeException("some error")); + up.onError(new RuntimeException("some error")); assertTrue(didRunOnTerminate.get()); } @@ -146,14 +146,14 @@ public void onTerminateCalledWhenOnError() { public void onTerminateCalledWhenOnComplete() { final AtomicBoolean didRunOnTerminate = new AtomicBoolean(); - UnicastProcessor us = UnicastProcessor.create(Observable.bufferSize(), new Runnable() { + UnicastProcessor up = UnicastProcessor.create(Observable.bufferSize(), new Runnable() { @Override public void run() { didRunOnTerminate.set(true); } }); assertFalse(didRunOnTerminate.get()); - us.onComplete(); + up.onComplete(); assertTrue(didRunOnTerminate.get()); } @@ -161,13 +161,13 @@ public void onTerminateCalledWhenOnComplete() { public void onTerminateCalledWhenCanceled() { final AtomicBoolean didRunOnTerminate = new AtomicBoolean(); - UnicastProcessor us = UnicastProcessor.create(Observable.bufferSize(), new Runnable() { + UnicastProcessor up = UnicastProcessor.create(Observable.bufferSize(), new Runnable() { @Override public void run() { didRunOnTerminate.set(true); } }); - final Disposable subscribe = us.subscribe(); + final Disposable subscribe = up.subscribe(); assertFalse(didRunOnTerminate.get()); subscribe.dispose(); @@ -268,7 +268,7 @@ public void onErrorStatePeeking() { public void rejectSyncFusion() { UnicastProcessor p = UnicastProcessor.create(); - TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.SYNC); + TestSubscriberEx ts = new TestSubscriberEx<>().setInitialFusionMode(QueueFuseable.SYNC); p.subscribe(ts); @@ -302,7 +302,7 @@ public void fusedDrainCancel() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final UnicastProcessor p = UnicastProcessor.create(); - final TestSubscriberEx ts = new TestSubscriberEx().setInitialFusionMode(QueueFuseable.ANY); + final TestSubscriberEx ts = new TestSubscriberEx<>().setInitialFusionMode(QueueFuseable.ANY); p.subscribe(ts); @@ -327,22 +327,22 @@ public void run() { @Test public void subscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final UnicastProcessor us = UnicastProcessor.create(); + final UnicastProcessor up = UnicastProcessor.create(); - final TestSubscriberEx ts1 = new TestSubscriberEx(); - final TestSubscriberEx ts2 = new TestSubscriberEx(); + final TestSubscriberEx ts1 = new TestSubscriberEx<>(); + final TestSubscriberEx ts2 = new TestSubscriberEx<>(); Runnable r1 = new Runnable() { @Override public void run() { - us.subscribe(ts1); + up.subscribe(ts1); } }; Runnable r2 = new Runnable() { @Override public void run() { - us.subscribe(ts2); + up.subscribe(ts2); } }; @@ -361,67 +361,67 @@ public void run() { @Test public void hasObservers() { - UnicastProcessor us = UnicastProcessor.create(); + UnicastProcessor up = UnicastProcessor.create(); - assertFalse(us.hasSubscribers()); + assertFalse(up.hasSubscribers()); - TestSubscriber ts = us.test(); + TestSubscriber ts = up.test(); - assertTrue(us.hasSubscribers()); + assertTrue(up.hasSubscribers()); ts.cancel(); - assertFalse(us.hasSubscribers()); + assertFalse(up.hasSubscribers()); } @Test public void drainFusedFailFast() { - UnicastProcessor us = UnicastProcessor.create(false); + UnicastProcessor up = UnicastProcessor.create(false); - TestSubscriberEx ts = us.to(TestHelper.testSubscriber(1, QueueFuseable.ANY, false)); + TestSubscriberEx ts = up.to(TestHelper.testSubscriber(1, QueueFuseable.ANY, false)); - us.done = true; - us.drainFused(ts); + up.done = true; + up.drainFused(ts); ts.assertResult(); } @Test public void drainFusedFailFastEmpty() { - UnicastProcessor us = UnicastProcessor.create(false); + UnicastProcessor up = UnicastProcessor.create(false); - TestSubscriberEx ts = us.to(TestHelper.testSubscriber(1, QueueFuseable.ANY, false)); + TestSubscriberEx ts = up.to(TestHelper.testSubscriber(1, QueueFuseable.ANY, false)); - us.drainFused(ts); + up.drainFused(ts); ts.assertEmpty(); } @Test public void checkTerminatedFailFastEmpty() { - UnicastProcessor us = UnicastProcessor.create(false); + UnicastProcessor up = UnicastProcessor.create(false); - TestSubscriberEx ts = us.to(TestHelper.testSubscriber(1, QueueFuseable.ANY, false)); + TestSubscriberEx ts = up.to(TestHelper.testSubscriber(1, QueueFuseable.ANY, false)); - us.checkTerminated(true, true, false, ts, us.queue); + up.checkTerminated(true, true, false, ts, up.queue); ts.assertEmpty(); } @Test public void alreadyCancelled() { - UnicastProcessor us = UnicastProcessor.create(false); + UnicastProcessor up = UnicastProcessor.create(false); - us.test().cancel(); + up.test().cancel(); BooleanSubscription bs = new BooleanSubscription(); - us.onSubscribe(bs); + up.onSubscribe(bs); assertTrue(bs.isCancelled()); List errors = TestHelper.trackPluginErrors(); try { - us.onError(new TestException()); + up.onError(new TestException()); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { @@ -431,9 +431,9 @@ public void alreadyCancelled() { @Test public void unicastSubscriptionBadRequest() { - UnicastProcessor us = UnicastProcessor.create(false); + UnicastProcessor up = UnicastProcessor.create(false); - UnicastProcessor.UnicastQueueSubscription usc = (UnicastProcessor.UnicastQueueSubscription)us.wip; + UnicastProcessor.UnicastQueueSubscription usc = (UnicastProcessor.UnicastQueueSubscription)up.wip; List errors = TestHelper.trackPluginErrors(); try { @@ -449,17 +449,17 @@ public void fusedNoConcurrentCleanDueToCancel() { for (int j = 0; j < TestHelper.RACE_LONG_LOOPS; j++) { List errors = TestHelper.trackPluginErrors(); try { - final UnicastProcessor us = UnicastProcessor.create(); + final UnicastProcessor up = UnicastProcessor.create(); - TestObserver to = us + TestObserver to = up .observeOn(Schedulers.io()) .map(Functions.identity()) .observeOn(Schedulers.single()) .firstOrError() .test(); - for (int i = 0; us.hasSubscribers(); i++) { - us.onNext(i); + for (int i = 0; up.hasSubscribers(); i++) { + up.onNext(i); } to diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerConcurrencyTests.java b/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerConcurrencyTests.java index 399a765305..ae7d1b24b0 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerConcurrencyTests.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerConcurrencyTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java b/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java index 4163c71ec6..2419fe557c 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/AbstractSchedulerTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -27,6 +27,7 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.disposables.SequentialDisposable; @@ -395,7 +396,7 @@ public void run() { } }); - ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); + ConcurrentObserverValidator observer = new ConcurrentObserverValidator<>(); // this should call onNext concurrently f.subscribe(observer); @@ -414,7 +415,7 @@ public final void observeOn() throws InterruptedException { Flowable f = Flowable.fromArray("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"); - ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); + ConcurrentObserverValidator observer = new ConcurrentObserverValidator<>(); f.observeOn(scheduler).subscribe(observer); @@ -449,7 +450,7 @@ public void subscribe(Subscriber subscriber) { } }); - ConcurrentObserverValidator observer = new ConcurrentObserverValidator(); + ConcurrentObserverValidator observer = new ConcurrentObserverValidator<>(); f.subscribe(observer); @@ -471,7 +472,7 @@ public void subscribe(Subscriber subscriber) { private static class ConcurrentObserverValidator extends DefaultSubscriber { final AtomicInteger concurrentCounter = new AtomicInteger(); - final AtomicReference error = new AtomicReference(); + final AtomicReference error = new AtomicReference<>(); final CountDownLatch completed = new CountDownLatch(1); @Override @@ -685,7 +686,7 @@ public void schedulePeriodicallyDirectDecoratesRunnable() throws InterruptedExce return; } - final AtomicReference disposable = new AtomicReference(); + final AtomicReference disposable = new AtomicReference<>(); try { assertRunnableDecorated(new Runnable() { @@ -771,4 +772,51 @@ public void schedulePeriodicallyDirectNullRunnable() { assertEquals("run is null", npe.getMessage()); } } + + void schedulePrint(Function onSchedule) { + CountDownLatch waitForBody = new CountDownLatch(1); + CountDownLatch waitForPrint = new CountDownLatch(1); + + try { + Disposable d = onSchedule.apply(() -> { + waitForBody.countDown(); + try { + waitForPrint.await(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + }); + + waitForBody.await(); + + assertNotEquals("", d.toString()); + } catch (Throwable ex) { + throw new AssertionError(ex); + } finally { + waitForPrint.countDown(); + } + } + + @Test + public void scheduleDirectPrint() { + if (getScheduler() instanceof TrampolineScheduler) { + // no concurrency with Trampoline + return; + } + schedulePrint(r -> getScheduler().scheduleDirect(r)); + } + + @Test + public void schedulePrint() { + if (getScheduler() instanceof TrampolineScheduler) { + // no concurrency with Trampoline + return; + } + Worker worker = getScheduler().createWorker(); + try { + schedulePrint(worker::schedule); + } finally { + worker.dispose(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java index 35be875a9a..b4576ebb53 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/CachedThreadSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,6 +24,7 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.schedulers.IoScheduler; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class CachedThreadSchedulerTest extends AbstractSchedulerConcurrencyTests { @@ -91,6 +92,7 @@ public void workerDisposed() { } @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; @@ -114,11 +116,11 @@ public void run() { Worker w = s.createWorker(); w.dispose(); - assertEquals(Disposables.disposed(), w.schedule(r)); + assertEquals(Disposable.disposed(), w.schedule(r)); - assertEquals(Disposables.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); - assertEquals(Disposables.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); assertEquals(0, calls[0]); } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java b/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java index 6d2aec07c9..c88415209d 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ComputationSchedulerTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -17,14 +17,17 @@ import java.util.HashMap; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.disposables.Disposables; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.schedulers.ComputationScheduler; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class ComputationSchedulerTests extends AbstractSchedulerConcurrencyTests { @@ -39,7 +42,7 @@ public void threadSafetyWhenSchedulerIsHoppingBetweenThreads() { final int NUM = 1000000; final CountDownLatch latch = new CountDownLatch(1); - final HashMap map = new HashMap(); + final HashMap map = new HashMap<>(); final Scheduler.Worker inner = Schedulers.computation().createWorker(); @@ -159,6 +162,7 @@ public void cancelledTaskRetention() throws InterruptedException { } @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; @@ -173,23 +177,148 @@ public void run() { s.shutdown(); s.shutdown(); - assertEquals(Disposables.disposed(), s.scheduleDirect(r)); + assertEquals(Disposable.disposed(), s.scheduleDirect(r)); - assertEquals(Disposables.disposed(), s.scheduleDirect(r, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), s.scheduleDirect(r, 1, TimeUnit.SECONDS)); - assertEquals(Disposables.disposed(), s.schedulePeriodicallyDirect(r, 1, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), s.schedulePeriodicallyDirect(r, 1, 1, TimeUnit.SECONDS)); Worker w = s.createWorker(); w.dispose(); assertTrue(w.isDisposed()); - assertEquals(Disposables.disposed(), w.schedule(r)); + assertEquals(Disposable.disposed(), w.schedule(r)); - assertEquals(Disposables.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); - assertEquals(Disposables.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); assertEquals(0, calls[0]); } + + @Test + public void exceptionFromObservableShouldNotBeSwallowed() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + + // #3 thread's uncaught exception handler + Scheduler computationScheduler = new ComputationScheduler(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setUncaughtExceptionHandler((thread, throwable) -> { + latch.countDown(); + }); + return t; + } + }); + + // #2 RxJava exception handler + RxJavaPlugins.setErrorHandler(h -> { + latch.countDown(); + }); + + // Exceptions, fatal or not, should be handled by + // #1 observer's onError(), or + // #2 RxJava exception handler, or + // #3 thread's uncaught exception handler, + // and should not be swallowed. + try { + + // #1 observer's onError() + Observable.create(s -> { + + s.onNext(1); + throw new OutOfMemoryError(); + }) + .subscribeOn(computationScheduler) + .subscribe(v -> { }, + e -> { latch.countDown(); } + ); + + assertTrue(latch.await(2, TimeUnit.SECONDS)); + } finally { + RxJavaPlugins.reset(); + computationScheduler.shutdown(); + } + } + + @Test + public void exceptionFromObserverShouldNotBeSwallowed() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + + // #3 thread's uncaught exception handler + Scheduler computationScheduler = new ComputationScheduler(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setUncaughtExceptionHandler((thread, throwable) -> { + latch.countDown(); + }); + return t; + } + }); + + // #2 RxJava exception handler + RxJavaPlugins.setErrorHandler(h -> { + latch.countDown(); + }); + + // Exceptions, fatal or not, should be handled by + // #1 observer's onError(), or + // #2 RxJava exception handler, or + // #3 thread's uncaught exception handler, + // and should not be swallowed. + try { + + // #1 observer's onError() + Flowable.interval(500, TimeUnit.MILLISECONDS, computationScheduler) + .subscribe(v -> { + throw new OutOfMemoryError(); + }, e -> { + latch.countDown(); + }); + + assertTrue(latch.await(2, TimeUnit.SECONDS)); + } finally { + RxJavaPlugins.reset(); + computationScheduler.shutdown(); + } + } + + @Test + @SuppressUndeliverable + public void periodicTaskShouldStopOnError() throws Exception { + AtomicInteger repeatCount = new AtomicInteger(); + + Schedulers.computation().schedulePeriodicallyDirect(new Runnable() { + @Override + public void run() { + repeatCount.incrementAndGet(); + throw new OutOfMemoryError(); + } + }, 0, 1, TimeUnit.MILLISECONDS); + + Thread.sleep(200); + + assertEquals(1, repeatCount.get()); + } + + @Test + @SuppressUndeliverable + public void periodicTaskShouldStopOnError2() throws Exception { + AtomicInteger repeatCount = new AtomicInteger(); + + Schedulers.computation().schedulePeriodicallyDirect(new Runnable() { + @Override + public void run() { + repeatCount.incrementAndGet(); + throw new OutOfMemoryError(); + } + }, 0, 1, TimeUnit.NANOSECONDS); + + Thread.sleep(200); + + assertEquals(1, repeatCount.get()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerFairTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerFairTest.java index 97fd1d3269..c01f0f9f1f 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerFairTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerFairTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -72,7 +72,7 @@ public void cancelledTaskRetention() throws InterruptedException { /** A simple executor which queues tasks and executes them one-by-one if executeOne() is called. */ static final class TestExecutor implements Executor { - final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); @Override public void execute(Runnable command) { queue.offer(command); diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerInterruptibleTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerInterruptibleTest.java index ecf50c3e99..074ac8039a 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerInterruptibleTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerInterruptibleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -69,7 +69,7 @@ public void cancelledTaskRetention() throws InterruptedException { /** A simple executor which queues tasks and executes them one-by-one if executeOne() is called. */ static final class TestExecutor implements Executor { - final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); @Override public void execute(Runnable command) { queue.offer(command); @@ -505,4 +505,609 @@ public void run() { worker.dispose(); } } + + @Test + public void interruptibleDirectTaskScheduledExecutor() throws Exception { + ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); + try { + Scheduler scheduler = Schedulers.from(exec, true); + + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = scheduler.scheduleDirect(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertTrue("Interruption did not propagate", isInterrupted.get()); + } finally { + exec.shutdown(); + } + } + + @Test + public void interruptibleWorkerTaskScheduledExecutor() throws Exception { + ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); + try { + Scheduler scheduler = Schedulers.from(exec, true); + + Worker worker = scheduler.createWorker(); + + try { + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = worker.schedule(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertTrue("Interruption did not propagate", isInterrupted.get()); + } finally { + worker.dispose(); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleDirectTask() throws Exception { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = scheduler.scheduleDirect(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleWorkerTask() throws Exception { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + Worker worker = scheduler.createWorker(); + + try { + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = worker.schedule(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + worker.dispose(); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleDirectTaskScheduledExecutor() throws Exception { + ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = scheduler.scheduleDirect(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleWorkerTaskScheduledExecutor() throws Exception { + ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + Worker worker = scheduler.createWorker(); + + try { + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = worker.schedule(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + worker.dispose(); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleDirectTaskTimed() throws Exception { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = scheduler.scheduleDirect(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }, 1, TimeUnit.MILLISECONDS); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleWorkerTaskTimed() throws Exception { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + Worker worker = scheduler.createWorker(); + + try { + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = worker.schedule(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }, 1, TimeUnit.MILLISECONDS); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + worker.dispose(); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleDirectTaskScheduledExecutorTimed() throws Exception { + ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = scheduler.scheduleDirect(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }, 1, TimeUnit.MILLISECONDS); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + exec.shutdown(); + } + } + + @Test + public void nonInterruptibleWorkerTaskScheduledExecutorTimed() throws Exception { + ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); + try { + Scheduler scheduler = Schedulers.from(exec, false); + + Worker worker = scheduler.createWorker(); + + try { + final AtomicInteger sync = new AtomicInteger(2); + + final AtomicBoolean isInterrupted = new AtomicBoolean(); + + Disposable d = worker.schedule(new Runnable() { + @Override + public void run() { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + isInterrupted.set(true); + } + } + }, 1, TimeUnit.MILLISECONDS); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + Thread.sleep(500); + + d.dispose(); + + int i = 20; + while (i-- > 0 && !isInterrupted.get()) { + Thread.sleep(50); + } + + assertFalse("Interruption happened", isInterrupted.get()); + } finally { + worker.dispose(); + } + } finally { + exec.shutdown(); + } + } + + public static class TrackInterruptScheduledExecutor extends ScheduledThreadPoolExecutor { + + public final AtomicBoolean interruptReceived = new AtomicBoolean(); + + public TrackInterruptScheduledExecutor() { + super(10); + } + + @Override + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + return new TrackingScheduledFuture(super.schedule(callable, delay, unit)); + } + + class TrackingScheduledFuture implements ScheduledFuture { + + ScheduledFuture original; + + TrackingScheduledFuture(ScheduledFuture original) { + this.original = original; + } + + @Override + public long getDelay(TimeUnit unit) { + return original.getDelay(unit); + } + + @Override + public int compareTo(Delayed o) { + return original.compareTo(o); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (mayInterruptIfRunning) { + interruptReceived.set(true); + } + return original.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return original.isCancelled(); + } + + @Override + public boolean isDone() { + return original.isDone(); + } + + @Override + public V get() throws InterruptedException, ExecutionException { + return original.get(); + } + + @Override + public V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return get(timeout, unit); + } + } + } + + @Test + public void noInterruptBeforeRunningDelayedWorker() throws Throwable { + TrackInterruptScheduledExecutor exec = new TrackInterruptScheduledExecutor(); + + try { + Scheduler sch = Schedulers.from(exec, false); + + Worker worker = sch.createWorker(); + + Disposable d = worker.schedule(() -> { }, 1, TimeUnit.SECONDS); + + d.dispose(); + + int i = 150; + + while (i-- > 0) { + assertFalse("Task interrupt detected", exec.interruptReceived.get()); + Thread.sleep(10); + } + + } finally { + exec.shutdownNow(); + } + } + + @Test + public void hasInterruptBeforeRunningDelayedWorker() throws Throwable { + TrackInterruptScheduledExecutor exec = new TrackInterruptScheduledExecutor(); + + try { + Scheduler sch = Schedulers.from(exec, true); + + Worker worker = sch.createWorker(); + + Disposable d = worker.schedule(() -> { }, 1, TimeUnit.SECONDS); + + d.dispose(); + + Thread.sleep(100); + assertTrue("Task interrupt detected", exec.interruptReceived.get()); + + } finally { + exec.shutdownNow(); + } + } + + @Test + public void noInterruptAfterRunningDelayedWorker() throws Throwable { + TrackInterruptScheduledExecutor exec = new TrackInterruptScheduledExecutor(); + + try { + Scheduler sch = Schedulers.from(exec, false); + + Worker worker = sch.createWorker(); + AtomicBoolean taskRun = new AtomicBoolean(); + + Disposable d = worker.schedule(() -> { + taskRun.set(true); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + exec.interruptReceived.set(true); + } + }, 100, TimeUnit.MILLISECONDS); + + Thread.sleep(150); + ; + d.dispose(); + + int i = 50; + + while (i-- > 0) { + assertFalse("Task interrupt detected", exec.interruptReceived.get()); + Thread.sleep(10); + } + + assertTrue("Task run at all", taskRun.get()); + + } finally { + exec.shutdownNow(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java index b24fa65f90..bb3e759884 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/ExecutorSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -18,14 +18,14 @@ import java.lang.management.*; import java.util.List; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.*; import org.junit.Test; import io.reactivex.rxjava3.core.Scheduler; import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.disposables.EmptyDisposable; +import io.reactivex.rxjava3.internal.disposables.*; import io.reactivex.rxjava3.internal.functions.Functions; import io.reactivex.rxjava3.internal.schedulers.*; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -93,7 +93,7 @@ public void run() { System.out.println("Wait before second GC"); System.out.println("JDK 6 purge is N log N because it removes and shifts one by one"); - int t = (int)(n * Math.log(n) / 100) + SchedulerPoolFactory.PURGE_PERIOD_SECONDS * 1000; + int t = (int)(n * Math.log(n) / 100) + 1000; int sleepStep = 100; while (t > 0) { System.out.printf(" >> Waiting for purge: %.2f s remaining%n", t / 1000d); @@ -155,7 +155,7 @@ public void cancelledTaskRetention() throws InterruptedException { /** A simple executor which queues tasks and executes them one-by-one if executeOne() is called. */ static final class TestExecutor implements Executor { - final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); @Override public void execute(Runnable command) { queue.offer(command); @@ -509,4 +509,42 @@ public void run() { assertSame(Functions.EMPTY_RUNNABLE, wrapper.getWrappedRunnable()); } + + @Test + public void interruptibleRunnableRunDisposeRace() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + Scheduler s = Schedulers.from(r -> exec.execute(r), true); + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + SequentialDisposable sd = new SequentialDisposable(); + + TestHelper.race( + () -> sd.update(s.scheduleDirect(() -> { })), + () -> sd.dispose() + ); + } + } finally { + exec.shutdown(); + } + } + + @Test + public void interruptibleRunnableRunDispose() { + try { + for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { + AtomicReference runRef = new AtomicReference<>(); + Scheduler s = Schedulers.from(r -> { + runRef.set(r); + }, true); + + Disposable d = s.scheduleDirect(() -> { }); + TestHelper.race( + () -> runRef.get().run(), + () -> d.dispose() + ); + } + } finally { + Thread.interrupted(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/FailOnBlockingTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/FailOnBlockingTest.java index d8891ddc0c..7cb4c15e07 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/FailOnBlockingTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/FailOnBlockingTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java index df114d23e8..37c7b85f87 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/NewThreadSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,6 +23,7 @@ import io.reactivex.rxjava3.core.Scheduler.Worker; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.internal.schedulers.NewThreadWorker; +import io.reactivex.rxjava3.testsupport.SuppressUndeliverable; public class NewThreadSchedulerTest extends AbstractSchedulerConcurrencyTests { @@ -37,6 +38,7 @@ public final void handledErrorIsNotDeliveredToThreadHandler() throws Interrupted } @Test + @SuppressUndeliverable public void shutdownRejects() { final int[] calls = { 0 }; @@ -53,11 +55,11 @@ public void run() { assertTrue(w.isDisposed()); - assertEquals(Disposables.disposed(), w.schedule(r)); + assertEquals(Disposable.disposed(), w.schedule(r)); - assertEquals(Disposables.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedule(r, 1, TimeUnit.SECONDS)); - assertEquals(Disposables.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); + assertEquals(Disposable.disposed(), w.schedulePeriodically(r, 1, 1, TimeUnit.SECONDS)); NewThreadWorker actual = (NewThreadWorker)w; @@ -75,6 +77,7 @@ public void run() { * @throws Exception on error */ @Test + @SuppressUndeliverable public void npeRegression() throws Exception { Scheduler s = getScheduler(); NewThreadWorker w = (NewThreadWorker) s.createWorker(); diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerLifecycleTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerLifecycleTest.java index 6a8eb53344..58b51254d8 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerLifecycleTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerLifecycleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public void shutdown() throws InterruptedException { System.out.println("testShutdown >> Giving time threads to spin-up"); Thread.sleep(500); - Set rxThreads = new HashSet(); + Set rxThreads = new HashSet<>(); for (Thread t : Thread.getAllStackTraces().keySet()) { if (t.getName().startsWith("Rx")) { rxThreads.add(t); @@ -108,7 +108,7 @@ public void startIdempotence() throws InterruptedException { System.out.println("testStartIdempotence >> giving some time"); Thread.sleep(500); - Set rxThreadsBefore = new HashSet(); + Set rxThreadsBefore = new HashSet<>(); for (Thread t : Thread.getAllStackTraces().keySet()) { if (t.getName().startsWith("Rx")) { rxThreadsBefore.add(t); @@ -120,7 +120,7 @@ public void startIdempotence() throws InterruptedException { System.out.println("testStartIdempotence >> giving some time again"); Thread.sleep(500); - Set rxThreadsAfter = new HashSet(); + Set rxThreadsAfter = new HashSet<>(); for (Thread t : Thread.getAllStackTraces().keySet()) { if (t.getName().startsWith("Rx")) { rxThreadsAfter.add(t); diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java index fa97b1c0e7..a9ec2205a7 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -61,18 +61,28 @@ public void run() { assertEquals(2, count[0]); } - @Test(expected = TestException.class) - public void periodicDirectThrows() { - TestScheduler scheduler = new TestScheduler(); + @Test + public void periodicDirectThrows() throws Throwable { + TestHelper.withErrorTracking(errors -> { + TestScheduler scheduler = new TestScheduler(); - scheduler.schedulePeriodicallyDirect(new Runnable() { - @Override - public void run() { - throw new TestException(); + try { + scheduler.schedulePeriodicallyDirect(new Runnable() { + @Override + public void run() { + throw new TestException(); + } + }, 100, 100, TimeUnit.MILLISECONDS); + + scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); + + fail("Should have thrown!"); + } catch (TestException expected) { + // expected } - }, 100, 100, TimeUnit.MILLISECONDS); - scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); + TestHelper.assertUndeliverable(errors, 0, TestException.class); + }); } @Test @@ -233,7 +243,7 @@ public void run() { Thread.sleep(250); - assertEquals(1, list.size()); + assertTrue(list.size() >= 1); TestHelper.assertUndeliverable(list, 0, TestException.class, null); } finally { diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java index 33c41e1189..3c899f262e 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerTestHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -28,12 +28,14 @@ private SchedulerTestHelper() { /** * Verifies that the given Scheduler does not deliver handled errors to its executing Thread's * {@link java.lang.Thread.UncaughtExceptionHandler}. + * + * @param scheduler {@link Scheduler} to verify. */ static void handledErrorIsNotDeliveredToThreadHandler(Scheduler scheduler) throws InterruptedException { Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler(); try { CapturingUncaughtExceptionHandler handler = new CapturingUncaughtExceptionHandler(); - CapturingObserver observer = new CapturingObserver(); + CapturingObserver observer = new CapturingObserver<>(); Thread.setDefaultUncaughtExceptionHandler(handler); IllegalStateException error = new IllegalStateException("Should be delivered to handler"); Flowable.error(error) diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerWorkerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerWorkerTest.java index 688e5fc429..17b7fa5dd7 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerWorkerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/SchedulerWorkerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -76,7 +76,7 @@ public void currentTimeDriftBackwards() throws Exception { Scheduler.Worker w = s.createWorker(); try { - final List times = new ArrayList(); + final List times = new ArrayList<>(); Disposable d = w.schedulePeriodically(new Runnable() { @Override @@ -118,7 +118,7 @@ public void currentTimeDriftForwards() throws Exception { Scheduler.Worker w = s.createWorker(); try { - final List times = new ArrayList(); + final List times = new ArrayList<>(); Disposable d = w.schedulePeriodically(new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java index 8bcb177053..4c2e776e0d 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/TestSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,6 +30,7 @@ import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; import io.reactivex.rxjava3.internal.util.ExceptionHelper; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.TestScheduler.*; public class TestSchedulerTest extends RxJavaTest { @@ -260,4 +261,95 @@ public void constructorTimeSetsTime() { assertEquals(5, ts.now(TimeUnit.SECONDS)); assertEquals(5000, ts.now(TimeUnit.MILLISECONDS)); } + + @Test + public void withOnScheduleHook() { + AtomicInteger run = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + RxJavaPlugins.setScheduleHandler(r -> { + counter.getAndIncrement(); + return r; + }); + try { + Runnable r = () -> run.getAndIncrement(); + TestScheduler ts = new TestScheduler(true); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(2, run.get()); + assertEquals(2, counter.get()); + + ts = new TestScheduler(); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(4, run.get()); + assertEquals(2, counter.get()); + } finally { + RxJavaPlugins.setScheduleHandler(null); + } + } + + @Test + public void withOnScheduleHookInitialTime() { + AtomicInteger run = new AtomicInteger(); + AtomicInteger counter = new AtomicInteger(); + RxJavaPlugins.setScheduleHandler(r -> { + counter.getAndIncrement(); + return r; + }); + try { + Runnable r = () -> run.getAndIncrement(); + TestScheduler ts = new TestScheduler(1, TimeUnit.HOURS, true); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(2, run.get()); + assertEquals(2, counter.get()); + + ts = new TestScheduler(1, TimeUnit.HOURS); + + ts.createWorker().schedule(r); + ts.createWorker().schedule(r, 1, TimeUnit.SECONDS); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(4, run.get()); + assertEquals(2, counter.get()); + } finally { + RxJavaPlugins.setScheduleHandler(null); + } + } + + @Test + public void disposeWork() { + AtomicInteger run = new AtomicInteger(); + Runnable r = () -> run.getAndIncrement(); + TestScheduler ts = new TestScheduler(1, TimeUnit.HOURS, true); + + Disposable d = ts.createWorker().schedule(r); + + assertFalse(d.isDisposed()); + + d.dispose(); + + assertTrue(d.isDisposed()); + + d.dispose(); + + assertTrue(d.isDisposed()); + + ts.advanceTimeBy(1, TimeUnit.SECONDS); + + assertEquals(0, run.get()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/TimedTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/TimedTest.java index 0fb0ce9767..7d82af675e 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/TimedTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/TimedTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public class TimedTest extends RxJavaTest { @Test public void properties() { - Timed timed = new Timed(1, 5, TimeUnit.SECONDS); + Timed timed = new Timed<>(1, 5, TimeUnit.SECONDS); assertEquals(1, timed.value().intValue()); assertEquals(5, timed.time()); @@ -35,22 +35,22 @@ public void properties() { @Test public void hashCodeOf() { - Timed t1 = new Timed(1, 5, TimeUnit.SECONDS); + Timed t1 = new Timed<>(1, 5, TimeUnit.SECONDS); assertEquals(TimeUnit.SECONDS.hashCode() + 31 * (5 + 31 * 1), t1.hashCode()); - Timed t2 = new Timed(null, 5, TimeUnit.SECONDS); + Timed t2 = new Timed<>(0, 5, TimeUnit.SECONDS); assertEquals(TimeUnit.SECONDS.hashCode() + 31 * (5 + 31 * 0), t2.hashCode()); } @Test public void equalsWith() { - Timed t1 = new Timed(1, 5, TimeUnit.SECONDS); - Timed t2 = new Timed(1, 5, TimeUnit.SECONDS); - Timed t3 = new Timed(2, 5, TimeUnit.SECONDS); - Timed t4 = new Timed(1, 4, TimeUnit.SECONDS); - Timed t5 = new Timed(1, 5, TimeUnit.MINUTES); + Timed t1 = new Timed<>(1, 5, TimeUnit.SECONDS); + Timed t2 = new Timed<>(1, 5, TimeUnit.SECONDS); + Timed t3 = new Timed<>(2, 5, TimeUnit.SECONDS); + Timed t4 = new Timed<>(1, 4, TimeUnit.SECONDS); + Timed t5 = new Timed<>(1, 5, TimeUnit.MINUTES); assertEquals(t1, t1); assertEquals(t1, t2); @@ -83,13 +83,13 @@ public void equalsWith() { @Test public void toStringOf() { - Timed t1 = new Timed(1, 5, TimeUnit.SECONDS); + Timed t1 = new Timed<>(1, 5, TimeUnit.SECONDS); assertEquals("Timed[time=5, unit=SECONDS, value=1]", t1.toString()); } @Test(expected = NullPointerException.class) public void timeUnitNullFail() throws Exception { - new Timed(1, 5, null); + new Timed<>(1, 5, null); } } diff --git a/src/test/java/io/reactivex/rxjava3/schedulers/TrampolineSchedulerTest.java b/src/test/java/io/reactivex/rxjava3/schedulers/TrampolineSchedulerTest.java index bfd7240db7..d98c93a435 100644 --- a/src/test/java/io/reactivex/rxjava3/schedulers/TrampolineSchedulerTest.java +++ b/src/test/java/io/reactivex/rxjava3/schedulers/TrampolineSchedulerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -63,7 +63,7 @@ public void accept(String t) { @Test public void nestedTrampolineWithUnsubscribe() { - final ArrayList workDone = new ArrayList(); + final ArrayList workDone = new ArrayList<>(); final CompositeDisposable workers = new CompositeDisposable(); Worker worker = Schedulers.trampoline().createWorker(); try { @@ -107,7 +107,7 @@ public void run() { public void trampolineWorkerHandlesConcurrentScheduling() { final Worker trampolineWorker = Schedulers.trampoline().createWorker(); final Subscriber subscriber = TestHelper.mockSubscriber(); - final TestSubscriber ts = new TestSubscriber(subscriber); + final TestSubscriber ts = new TestSubscriber<>(subscriber); // Spam the trampoline with actions. Flowable.range(0, 50) diff --git a/src/test/java/io/reactivex/rxjava3/single/SingleCacheTest.java b/src/test/java/io/reactivex/rxjava3/single/SingleCacheTest.java index 16221a253f..3fe2da558c 100644 --- a/src/test/java/io/reactivex/rxjava3/single/SingleCacheTest.java +++ b/src/test/java/io/reactivex/rxjava3/single/SingleCacheTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -89,7 +89,7 @@ public void crossCancel() { PublishSubject ps = PublishSubject.create(); Single cache = ps.single(-99).cache(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override @@ -114,7 +114,7 @@ public void crossCancelOnError() { PublishSubject ps = PublishSubject.create(); Single cache = ps.single(-99).cache(); - final TestSubscriber ts1 = new TestSubscriber(); + final TestSubscriber ts1 = new TestSubscriber<>(); TestSubscriber ts2 = new TestSubscriber() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/single/SingleNullTests.java b/src/test/java/io/reactivex/rxjava3/single/SingleNullTests.java index 8210632567..058ecdc6b0 100644 --- a/src/test/java/io/reactivex/rxjava3/single/SingleNullTests.java +++ b/src/test/java/io/reactivex/rxjava3/single/SingleNullTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,6 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.schedulers.Schedulers; public class SingleNullTests extends RxJavaTest { @@ -34,11 +33,6 @@ public class SingleNullTests extends RxJavaTest { Single error = Single.error(new TestException()); - @Test(expected = NullPointerException.class) - public void ambIterableNull() { - Single.amb((Iterable>)null); - } - @Test public void ambIterableIteratorNull() { Single.amb(new Iterable>() { @@ -49,7 +43,6 @@ public Iterator> iterator() { }).test().assertError(NullPointerException.class); } - @SuppressWarnings("unchecked") @Test public void ambIterableOneIsNull() { Single.amb(Arrays.asList(null, just1)) @@ -57,12 +50,6 @@ public void ambIterableOneIsNull() { .assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void ambArrayNull() { - Single.ambArray((Single[])null); - } - - @SuppressWarnings("unchecked") @Test public void ambArrayOneIsNull() { Single.ambArray(null, just1) @@ -70,11 +57,6 @@ public void ambArrayOneIsNull() { .assertError(NullPointerException.class); } - @Test(expected = NullPointerException.class) - public void concatIterableNull() { - Single.concat((Iterable>)null); - } - @Test(expected = NullPointerException.class) public void concatIterableIteratorNull() { Single.concat(new Iterable>() { @@ -85,17 +67,11 @@ public Iterator> iterator() { }).blockingSubscribe(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void concatIterableOneIsNull() { Single.concat(Arrays.asList(just1, null)).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void concatObservableNull() { - Single.concat((Flowable>)null); - } - @Test public void concatNull() throws Exception { int maxArgs = 4; @@ -125,41 +101,16 @@ public void concatNull() throws Exception { } } - @Test(expected = NullPointerException.class) - public void createNull() { - Single.unsafeCreate(null); - } - - @Test(expected = NullPointerException.class) - public void deferNull() { - Single.defer(null); - } - @Test(expected = NullPointerException.class) public void deferReturnsNull() { Single.defer(Functions.>nullSupplier()).blockingGet(); } - @Test(expected = NullPointerException.class) - public void errorSupplierNull() { - Single.error((Supplier)null); - } - @Test(expected = NullPointerException.class) public void errorSupplierReturnsNull() { Single.error(Functions.nullSupplier()).blockingGet(); } - @Test(expected = NullPointerException.class) - public void errorNull() { - Single.error((Throwable)null); - } - - @Test(expected = NullPointerException.class) - public void fromCallableNull() { - Single.fromCallable(null); - } - @Test(expected = NullPointerException.class) public void fromCallableReturnsNull() { Single.fromCallable(new Callable() { @@ -170,75 +121,20 @@ public Object call() throws Exception { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void fromFutureNull() { - Single.fromFuture((Future)null); - } - @Test(expected = NullPointerException.class) public void fromFutureReturnsNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); f.run(); Single.fromFuture(f).blockingGet(); } - @Test(expected = NullPointerException.class) - public void fromFutureTimedFutureNull() { - Single.fromFuture(null, 1, TimeUnit.SECONDS); - } - - @Test(expected = NullPointerException.class) - public void fromFutureTimedUnitNull() { - Single.fromFuture(new FutureTask(new Callable() { - @Override - public Object call() throws Exception { - return null; - } - }), 1, null); - } - - @Test(expected = NullPointerException.class) - public void fromFutureTimedSchedulerNull() { - Single.fromFuture(new FutureTask(new Callable() { - @Override - public Object call() throws Exception { - return null; - } - }), 1, TimeUnit.SECONDS, null); - } - @Test(expected = NullPointerException.class) public void fromFutureTimedReturnsNull() { - FutureTask f = new FutureTask(Functions.EMPTY_RUNNABLE, null); + FutureTask f = new FutureTask<>(Functions.EMPTY_RUNNABLE, null); f.run(); Single.fromFuture(f, 1, TimeUnit.SECONDS).blockingGet(); } - @Test(expected = NullPointerException.class) - public void fromFutureSchedulerNull() { - Single.fromFuture(new FutureTask(new Callable() { - @Override - public Object call() throws Exception { - return null; - } - }), null); - } - - @Test(expected = NullPointerException.class) - public void fromPublisherNull() { - Single.fromPublisher(null); - } - - @Test(expected = NullPointerException.class) - public void justNull() { - Single.just(null); - } - - @Test(expected = NullPointerException.class) - public void mergeIterableNull() { - Single.merge((Iterable>)null); - } - @Test(expected = NullPointerException.class) public void mergeIterableIteratorNull() { Single.merge(new Iterable>() { @@ -249,17 +145,11 @@ public Iterator> iterator() { }).blockingSubscribe(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void mergeIterableOneIsNull() { Single.merge(Arrays.asList(null, just1)).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void mergeSingleNull() { - Single.merge((Single>)null); - } - @Test public void mergeNull() throws Exception { int maxArgs = 4; @@ -289,46 +179,6 @@ public void mergeNull() throws Exception { } } - @Test(expected = NullPointerException.class) - public void timerUnitNull() { - Single.timer(1, null); - } - - @Test(expected = NullPointerException.class) - public void timerSchedulerNull() { - Single.timer(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void equalsFirstNull() { - Single.equals(null, just1); - } - - @Test(expected = NullPointerException.class) - public void equalsSecondNull() { - Single.equals(just1, null); - } - - @Test(expected = NullPointerException.class) - public void usingResourceSupplierNull() { - Single.using(null, new Function>() { - @Override - public Single apply(Object d) { - return just1; - } - }, Functions.emptyConsumer()); - } - - @Test(expected = NullPointerException.class) - public void usingSingleSupplierNull() { - Single.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, null, Functions.emptyConsumer()); - } - @Test(expected = NullPointerException.class) public void usingSingleSupplierReturnsNull() { Single.using(new Supplier() { @@ -344,31 +194,6 @@ public Single apply(Object d) { }, Functions.emptyConsumer()).blockingGet(); } - @Test(expected = NullPointerException.class) - public void usingDisposeNull() { - Single.using(new Supplier() { - @Override - public Object get() { - return 1; - } - }, new Function>() { - @Override - public Single apply(Object d) { - return just1; - } - }, null); - } - - @Test(expected = NullPointerException.class) - public void zipIterableNull() { - Single.zip((Iterable>)null, new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }); - } - @Test(expected = NullPointerException.class) public void zipIterableIteratorNull() { Single.zip(new Iterable>() { @@ -384,7 +209,6 @@ public Object apply(Object[] v) { }).blockingGet(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableOneIsNull() { Single.zip(Arrays.asList(null, just1), new Function() { @@ -395,13 +219,6 @@ public Object apply(Object[] v) { }).blockingGet(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void zipIterableOneFunctionNull() { - Single.zip(Arrays.asList(just1, just1), null).blockingGet(); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableOneFunctionReturnsNull() { Single.zip(Arrays.asList(just1, just1), new Function() { @@ -484,52 +301,6 @@ public Object invoke(Object o, Method m1, Object[] a) throws Throwable { } } - @Test(expected = NullPointerException.class) - public void zip2FirstNull() { - Single.zip(null, just1, new BiFunction() { - @Override - public Object apply(Object a, Integer b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void zip2SecondNull() { - Single.zip(just1, null, new BiFunction() { - @Override - public Object apply(Integer a, Object b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void zip2ZipperNull() { - Single.zip(just1, just1, null); - } - - @Test(expected = NullPointerException.class) - public void zip2ZipperReturnsdNull() { - Single.zip(just1, null, new BiFunction() { - @Override - public Object apply(Integer a, Object b) { - return null; - } - }).blockingGet(); - } - - @Test(expected = NullPointerException.class) - public void zipArrayNull() { - Single.zipArray(new Function() { - @Override - public Object apply(Object[] v) { - return 1; - } - }, (Single[])null); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipIterableTwoIsNull() { Single.zip(Arrays.asList(just1, null), new Function() { @@ -541,7 +312,6 @@ public Object apply(Object[] v) { .blockingGet(); } - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipArrayOneIsNull() { Single.zipArray(new Function() { @@ -553,13 +323,6 @@ public Object apply(Object[] v) { .blockingGet(); } - @SuppressWarnings("unchecked") - @Test(expected = NullPointerException.class) - public void zipArrayFunctionNull() { - Single.zipArray(null, just1, just1); - } - - @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void zipArrayFunctionReturnsNull() { Single.zipArray(new Function() { @@ -574,61 +337,6 @@ public Object apply(Object[] v) { // Instance methods //************************************************** - @Test(expected = NullPointerException.class) - public void ambWithNull() { - just1.ambWith(null); - } - - @Test(expected = NullPointerException.class) - public void composeNull() { - just1.compose(null); - } - - @Test(expected = NullPointerException.class) - public void castNull() { - just1.cast(null); - } - - @Test(expected = NullPointerException.class) - public void concatWith() { - just1.concatWith(null); - } - - @Test(expected = NullPointerException.class) - public void delayUnitNull() { - just1.delay(1, null); - } - - @Test(expected = NullPointerException.class) - public void delaySchedulerNull() { - just1.delay(1, TimeUnit.SECONDS, null); - } - - @Test(expected = NullPointerException.class) - public void doOnSubscribeNull() { - just1.doOnSubscribe(null); - } - - @Test(expected = NullPointerException.class) - public void doOnSuccess() { - just1.doOnSuccess(null); - } - - @Test(expected = NullPointerException.class) - public void doOnError() { - error.doOnError(null); - } - - @Test(expected = NullPointerException.class) - public void doOnDisposeNull() { - just1.doOnDispose(null); - } - - @Test(expected = NullPointerException.class) - public void flatMapNull() { - just1.flatMap(null); - } - @Test(expected = NullPointerException.class) public void flatMapFunctionReturnsNull() { just1.flatMap(new Function>() { @@ -639,11 +347,6 @@ public Single apply(Integer v) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void flatMapPublisherNull() { - just1.flatMapPublisher(null); - } - @Test(expected = NullPointerException.class) public void flatMapPublisherFunctionReturnsNull() { just1.flatMapPublisher(new Function>() { @@ -654,11 +357,6 @@ public Publisher apply(Integer v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void liftNull() { - just1.lift(null); - } - @Test(expected = NullPointerException.class) public void liftFunctionReturnsNull() { just1.lift(new SingleOperator() { @@ -669,31 +367,6 @@ public SingleObserver apply(SingleObserver obse }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void containsNull() { - just1.contains(null); - } - - @Test(expected = NullPointerException.class) - public void containsComparerNull() { - just1.contains(1, null); - } - - @Test(expected = NullPointerException.class) - public void mergeWithNull() { - just1.mergeWith(null); - } - - @Test(expected = NullPointerException.class) - public void observeOnNull() { - just1.observeOn(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorReturnSupplierNull() { - just1.onErrorReturn((Function)null); - } - @Test(expected = NullPointerException.class) public void onErrorReturnsSupplierReturnsNull() { error.onErrorReturn(new Function() { @@ -704,21 +377,6 @@ public Integer apply(Throwable t) throws Exception { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void onErrorReturnValueNull() { - error.onErrorReturnItem(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorResumeWithSingleNull() { - error.onErrorResumeWith(null); - } - - @Test(expected = NullPointerException.class) - public void onErrorResumeNextNull() { - error.onErrorResumeNext(null); - } - @Test public void onErrorResumeNextFunctionReturnsNull() { try { @@ -733,11 +391,6 @@ public Single apply(Throwable e) { } } - @Test(expected = NullPointerException.class) - public void repeatWhenNull() { - error.repeatWhen(null); - } - @Test(expected = NullPointerException.class) public void repeatWhenFunctionReturnsNull() { error.repeatWhen(new Function, Publisher>() { @@ -748,26 +401,6 @@ public Publisher apply(Flowable v) { }).blockingSubscribe(); } - @Test(expected = NullPointerException.class) - public void repeatUntilNull() { - error.repeatUntil(null); - } - - @Test(expected = NullPointerException.class) - public void retryBiPreducateNull() { - error.retry((BiPredicate)null); - } - - @Test(expected = NullPointerException.class) - public void retryPredicateNull() { - error.retry((Predicate)null); - } - - @Test(expected = NullPointerException.class) - public void retryWhenNull() { - error.retryWhen(null); - } - @Test(expected = NullPointerException.class) public void retryWhenFunctionReturnsNull() { error.retryWhen(new Function, Publisher>() { @@ -778,21 +411,6 @@ public Publisher apply(Flowable e) { }).blockingGet(); } - @Test(expected = NullPointerException.class) - public void subscribeBiConsumerNull() { - just1.subscribe((BiConsumer)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeConsumerNull() { - just1.subscribe((Consumer)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeSingeSubscriberNull() { - just1.subscribe((SingleObserver)null); - } - @Test(expected = NullPointerException.class) public void subscribeOnSuccessNull() { just1.subscribe(null, new Consumer() { @@ -801,64 +419,6 @@ public void accept(Throwable e) { } }); } - @Test(expected = NullPointerException.class) - public void subscribeOnErrorNull() { - just1.subscribe(new Consumer() { - @Override - public void accept(Integer v) { } - }, null); - } - - @Test(expected = NullPointerException.class) - public void subscribeSubscriberNull() { - just1.toFlowable().subscribe((Subscriber)null); - } - - @Test(expected = NullPointerException.class) - public void subscribeOnNull() { - just1.subscribeOn(null); - } - - @Test(expected = NullPointerException.class) - public void timeoutUnitNull() { - just1.timeout(1, null); - } - - @Test(expected = NullPointerException.class) - public void timeoutSchedulerNull() { - just1.timeout(1, TimeUnit.SECONDS, (Scheduler)null); - } - - @Test(expected = NullPointerException.class) - public void timeoutOtherNull() { - just1.timeout(1, TimeUnit.SECONDS, Schedulers.single(), null); - } - - @Test(expected = NullPointerException.class) - public void timeoutOther2Null() { - just1.timeout(1, TimeUnit.SECONDS, (Single)null); - } - - @Test(expected = NullPointerException.class) - public void toNull() { - just1.to(null); - } - - @Test(expected = NullPointerException.class) - public void zipWithNull() { - just1.zipWith(null, new BiFunction() { - @Override - public Object apply(Integer a, Object b) { - return 1; - } - }); - } - - @Test(expected = NullPointerException.class) - public void zipWithFunctionNull() { - just1.zipWith(just1, null); - } - @Test(expected = NullPointerException.class) public void zipWithFunctionReturnsNull() { just1.zipWith(just1, new BiFunction() { diff --git a/src/test/java/io/reactivex/rxjava3/single/SingleRetryTest.java b/src/test/java/io/reactivex/rxjava3/single/SingleRetryTest.java index 1d5a1bff33..e38322a4ab 100644 --- a/src/test/java/io/reactivex/rxjava3/single/SingleRetryTest.java +++ b/src/test/java/io/reactivex/rxjava3/single/SingleRetryTest.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-present, RxJava Contributors. +/* + * 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 @@ -21,6 +21,7 @@ import org.junit.Test; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Predicate; import io.reactivex.rxjava3.internal.functions.Functions; @@ -120,4 +121,42 @@ public void retryTimesPredicateWithZeroRetries() { assertEquals(1, numberOfSubscribeCalls.get()); } + + @Test + public void untilTrueJust() { + Single.just(1) + .retryUntil(() -> true) + .test() + .assertResult(1); + } + + @Test + public void untilFalseJust() { + Single.just(1) + .retryUntil(() -> false) + .test() + .assertResult(1); + } + + @Test + public void untilTrueError() { + Single.error(new TestException()) + .retryUntil(() -> true) + .test() + .assertFailure(TestException.class); + } + + @Test + public void untilFalseError() { + AtomicInteger counter = new AtomicInteger(); + Single.defer(() -> { + if (counter.getAndIncrement() == 0) { + return Single.error(new TestException()); + } + return Single.just(1); + }) + .retryUntil(() -> false) + .test() + .assertResult(1); + } } diff --git a/src/test/java/io/reactivex/rxjava3/single/SingleSubscribeTest.java b/src/test/java/io/reactivex/rxjava3/single/SingleSubscribeTest.java index 68484d63c1..df363bee83 100644 --- a/src/test/java/io/reactivex/rxjava3/single/SingleSubscribeTest.java +++ b/src/test/java/io/reactivex/rxjava3/single/SingleSubscribeTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/single/SingleTest.java b/src/test/java/io/reactivex/rxjava3/single/SingleTest.java index 0e99b0b26d..7b51c97471 100644 --- a/src/test/java/io/reactivex/rxjava3/single/SingleTest.java +++ b/src/test/java/io/reactivex/rxjava3/single/SingleTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -35,14 +35,14 @@ public class SingleTest extends RxJavaTest { @Test public void helloWorld() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single.just("Hello World!").toFlowable().subscribe(ts); ts.assertValueSequence(Arrays.asList("Hello World!")); } @Test public void helloWorld2() { - final AtomicReference v = new AtomicReference(); + final AtomicReference v = new AtomicReference<>(); Single.just("Hello World!").subscribe(new SingleObserver() { @Override @@ -66,7 +66,7 @@ public void onError(Throwable error) { @Test public void map() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single.just("A") .map(new Function() { @Override @@ -80,7 +80,7 @@ public String apply(String s) { @Test public void zip() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single a = Single.just("A"); Single b = Single.just("B"); @@ -96,7 +96,7 @@ public String apply(String a1, String b1) { @Test public void zipWith() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single.just("A").zipWith(Single.just("B"), new BiFunction() { @Override @@ -110,7 +110,7 @@ public String apply(String a1, String b1) { @Test public void merge() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single a = Single.just("A"); Single b = Single.just("B"); @@ -120,7 +120,7 @@ public void merge() { @Test public void mergeWith() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single.just("A").mergeWith(Single.just("B")).subscribe(ts); ts.assertValueSequence(Arrays.asList("A", "B")); @@ -128,12 +128,12 @@ public void mergeWith() { @Test public void createSuccess() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single.unsafeCreate(new SingleSource() { @Override public void subscribe(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onSuccess("Hello"); } }).toFlowable().subscribe(ts); @@ -143,11 +143,11 @@ public void subscribe(SingleObserver observer) { @Test public void createError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Single.unsafeCreate(new SingleSource() { @Override public void subscribe(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); observer.onError(new RuntimeException("fail")); } }).toFlowable().subscribe(ts); @@ -158,7 +158,7 @@ public void subscribe(SingleObserver observer) { @Test public void async() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single.just("Hello") .subscribeOn(Schedulers.io()) .map(new Function() { @@ -183,7 +183,7 @@ public String apply(String v) { @Test public void flatMap() throws InterruptedException { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Single.just("Hello").flatMap(new Function>() { @Override public Single apply(String s) { @@ -200,11 +200,11 @@ public Single apply(String s) { @Test public void timeout() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single s1 = Single.unsafeCreate(new SingleSource() { @Override public void subscribe(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); try { Thread.sleep(5000); } catch (InterruptedException e) { @@ -222,11 +222,11 @@ public void subscribe(SingleObserver observer) { @Test public void timeoutWithFallback() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Single s1 = Single.unsafeCreate(new SingleSource() { @Override public void subscribe(SingleObserver observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); try { Thread.sleep(5000); } catch (InterruptedException e) { @@ -245,7 +245,7 @@ public void subscribe(SingleObserver observer) { @Test public void unsubscribe() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicBoolean unsubscribed = new AtomicBoolean(); final AtomicBoolean interrupted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(2); @@ -269,7 +269,7 @@ public void run() { } }); - sd.replace(Disposables.fromRunnable(new Runnable() { + sd.replace(Disposable.fromRunnable(new Runnable() { @Override public void run() { unsubscribed.set(true); @@ -343,7 +343,7 @@ public void run() { } }); - sd.replace(Disposables.fromRunnable(new Runnable() { + sd.replace(Disposable.fromRunnable(new Runnable() { @Override public void run() { unsubscribed.set(true); @@ -399,7 +399,7 @@ public void run() { } }); - sd.replace(Disposables.fromRunnable(new Runnable() { + sd.replace(Disposable.fromRunnable(new Runnable() { @Override public void run() { unsubscribed.set(true); @@ -431,12 +431,12 @@ public void backpressureAsObservable() { Single s = Single.unsafeCreate(new SingleSource() { @Override public void subscribe(SingleObserver t) { - t.onSubscribe(Disposables.empty()); + t.onSubscribe(Disposable.empty()); t.onSuccess("hello"); } }); - TestSubscriber ts = new TestSubscriber(0L); + TestSubscriber ts = new TestSubscriber<>(0L); s.toFlowable().subscribe(ts); @@ -450,7 +450,7 @@ public void subscribe(SingleObserver t) { @Test public void toObservable() { Flowable a = Single.just("a").toFlowable(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); a.subscribe(ts); ts.assertValue("a"); ts.assertNoErrors(); @@ -510,7 +510,6 @@ public void toFutureThrows() throws Exception { @Test(expected = UnsupportedOperationException.class) public void toFlowableIterableRemove() { - @SuppressWarnings("unchecked") Iterable> f = SingleInternalHelper.iterableToFlowable(Arrays.asList(Single.just(1))); Iterator> iterator = f.iterator(); @@ -520,7 +519,6 @@ public void toFlowableIterableRemove() { @Test public void zipIterableObject() { - @SuppressWarnings("unchecked") final List> singles = Arrays.asList(Single.just(1), Single.just(4)); Single.zip(singles, new Function() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/single/SingleTimerTest.java b/src/test/java/io/reactivex/rxjava3/single/SingleTimerTest.java index c8624b710a..7fefa15467 100644 --- a/src/test/java/io/reactivex/rxjava3/single/SingleTimerTest.java +++ b/src/test/java/io/reactivex/rxjava3/single/SingleTimerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java index 7a7e381bf9..8228b74b5d 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/AsyncSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,11 +24,11 @@ import org.mockito.*; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.testsupport.*; public class AsyncSubjectTest extends SubjectTest { @@ -112,6 +112,7 @@ public void subscribeAfterError() { } @Test + @SuppressUndeliverable public void error() { AsyncSubject subject = AsyncSubject.create(); @@ -136,7 +137,7 @@ public void unsubscribeBeforeCompleted() { AsyncSubject subject = AsyncSubject.create(); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); subject.subscribe(to); subject.onNext("one"); @@ -185,7 +186,7 @@ public void subscribeCompletionRaceCondition() { */ for (int i = 0; i < 50; i++) { final AsyncSubject subject = AsyncSubject.create(); - final AtomicReference value1 = new AtomicReference(); + final AtomicReference value1 = new AtomicReference<>(); subject.subscribe(new Consumer() { @@ -243,7 +244,7 @@ public void run() { private static class SubjectSubscriberThread extends Thread { private final AsyncSubject subject; - private final AtomicReference value = new AtomicReference(); + private final AtomicReference value = new AtomicReference<>(); SubjectSubscriberThread(AsyncSubject subject) { this.subject = subject; @@ -327,7 +328,7 @@ public void currentStateMethodsError() { @Test public void fusionLive() { - AsyncSubject ap = new AsyncSubject(); + AsyncSubject ap = new AsyncSubject<>(); TestObserverEx to = ap.to(TestHelper.testConsumer(false, QueueFuseable.ANY)); @@ -347,7 +348,7 @@ public void fusionLive() { @Test public void fusionOfflie() { - AsyncSubject ap = new AsyncSubject(); + AsyncSubject ap = new AsyncSubject<>(); ap.onNext(1); ap.onComplete(); @@ -362,14 +363,14 @@ public void fusionOfflie() { public void onSubscribeAfterDone() { AsyncSubject p = AsyncSubject.create(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); p.onSubscribe(bs); assertFalse(bs.isDisposed()); p.onComplete(); - bs = Disposables.empty(); + bs = Disposable.empty(); p.onSubscribe(bs); assertTrue(bs.isDisposed()); @@ -418,6 +419,7 @@ public void run() { } @Test + @SuppressUndeliverable public void onErrorCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { @@ -455,7 +457,7 @@ public void run() { public void onNextCrossCancel() { AsyncSubject p = AsyncSubject.create(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); TestObserver to1 = new TestObserver() { @Override public void onNext(Object t) { @@ -475,10 +477,11 @@ public void onNext(Object t) { } @Test + @SuppressUndeliverable public void onErrorCrossCancel() { AsyncSubject p = AsyncSubject.create(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); TestObserver to1 = new TestObserver() { @Override public void onError(Throwable t) { @@ -500,7 +503,7 @@ public void onError(Throwable t) { public void onCompleteCrossCancel() { AsyncSubject p = AsyncSubject.create(); - final TestObserver to2 = new TestObserver(); + final TestObserver to2 = new TestObserver<>(); TestObserver to1 = new TestObserver() { @Override public void onComplete() { @@ -517,4 +520,9 @@ public void onComplete() { to1.assertResult(); to2.assertEmpty(); } + + @Test + public void dispose() { + TestHelper.checkDisposed(AsyncSubject.create()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java index af73a522aa..8871ee8e7e 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/BehaviorSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -136,7 +136,7 @@ public void completedStopsEmittingData() { Observer observerB = TestHelper.mockObserver(); Observer observerC = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observerA); + TestObserver to = new TestObserver<>(observerA); channel.subscribe(to); channel.subscribe(observerB); @@ -389,7 +389,7 @@ public void run() { } }); - final AtomicReference o = new AtomicReference(); + final AtomicReference o = new AtomicReference<>(); rs.subscribeOn(s).observeOn(Schedulers.io()) .subscribe(new DefaultObserver() { @@ -542,7 +542,7 @@ public void cancelOnArrival() { public void onSubscribe() { BehaviorSubject p = BehaviorSubject.create(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); p.onSubscribe(bs); @@ -550,7 +550,7 @@ public void onSubscribe() { p.onComplete(); - bs = Disposables.empty(); + bs = Disposable.empty(); p.onSubscribe(bs); @@ -679,7 +679,7 @@ public void completeSubscribeRace() throws Exception { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final BehaviorSubject p = BehaviorSubject.create(); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Runnable r1 = new Runnable() { @Override @@ -706,7 +706,7 @@ public void errorSubscribeRace() throws Exception { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final BehaviorSubject p = BehaviorSubject.create(); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final TestException ex = new TestException(); @@ -735,9 +735,9 @@ public void behaviorDisposableDisposeState() { BehaviorSubject bs = BehaviorSubject.create(); bs.onNext(1); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - BehaviorDisposable bd = new BehaviorDisposable(to, bs); + BehaviorDisposable bd = new BehaviorDisposable<>(to, bs); to.onSubscribe(bd); assertFalse(bd.isDisposed()); @@ -766,9 +766,9 @@ public void emitFirstDisposeRace() { BehaviorSubject bs = BehaviorSubject.create(); bs.onNext(1); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final BehaviorDisposable bd = new BehaviorDisposable(to, bs); + final BehaviorDisposable bd = new BehaviorDisposable<>(to, bs); to.onSubscribe(bd); Runnable r1 = new Runnable() { @@ -795,9 +795,9 @@ public void emitNextDisposeRace() { BehaviorSubject bs = BehaviorSubject.create(); bs.onNext(1); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final BehaviorDisposable bd = new BehaviorDisposable(to, bs); + final BehaviorDisposable bd = new BehaviorDisposable<>(to, bs); to.onSubscribe(bd); Runnable r1 = new Runnable() { @@ -822,9 +822,9 @@ public void emittingEmitNext() { BehaviorSubject bs = BehaviorSubject.create(); bs.onNext(1); - TestObserver to = new TestObserver(); + TestObserver to = new TestObserver<>(); - final BehaviorDisposable bd = new BehaviorDisposable(to, bs); + final BehaviorDisposable bd = new BehaviorDisposable<>(to, bs); to.onSubscribe(bd); bd.emitting = true; @@ -833,4 +833,19 @@ public void emittingEmitNext() { assertNotNull(bd.queue); } + + @Test + public void hasObservers() { + BehaviorSubject bs = BehaviorSubject.create(); + + assertFalse(bs.hasObservers()); + + TestObserver to = bs.test(); + + assertTrue(bs.hasObservers()); + + to.dispose(); + + assertFalse(bs.hasObservers()); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/CompletableSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/CompletableSubjectTest.java index 34aaff3831..6b5a7a6085 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/CompletableSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/CompletableSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -189,7 +189,7 @@ public void onComplete() { public void onSubscribeDispose() { CompletableSubject cs = CompletableSubject.create(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); cs.onSubscribe(d); @@ -197,7 +197,7 @@ public void onSubscribeDispose() { cs.onComplete(); - d = Disposables.empty(); + d = Disposable.empty(); cs.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/subjects/MaybeSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/MaybeSubjectTest.java index 8ff3f7b1b5..c741f02508 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/MaybeSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/MaybeSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -234,7 +234,7 @@ public void onComplete() { public void onSubscribeDispose() { MaybeSubject ms = MaybeSubject.create(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ms.onSubscribe(d); @@ -242,7 +242,7 @@ public void onSubscribeDispose() { ms.onComplete(); - d = Disposables.empty(); + d = Disposable.empty(); ms.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java index 43ebb533d5..a4b464c512 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/PublishSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -29,7 +29,7 @@ import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.observers.*; -import io.reactivex.rxjava3.testsupport.TestHelper; +import io.reactivex.rxjava3.testsupport.*; public class PublishSubjectTest extends SubjectTest { @@ -39,6 +39,7 @@ protected Subject create() { } @Test + @SuppressUndeliverable public void completed() { PublishSubject subject = PublishSubject.create(); @@ -68,7 +69,7 @@ public void completedStopsEmittingData() { Observer observerB = TestHelper.mockObserver(); Observer observerC = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observerA); + TestObserver to = new TestObserver<>(observerA); channel.subscribe(to); channel.subscribe(observerB); @@ -112,6 +113,7 @@ private void assertCompletedSubscriber(Observer observer) { } @Test + @SuppressUndeliverable public void error() { PublishSubject subject = PublishSubject.create(); @@ -177,7 +179,7 @@ public void unsubscribeFirstSubscriber() { PublishSubject subject = PublishSubject.create(); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); subject.subscribe(to); subject.onNext("one"); @@ -212,7 +214,7 @@ public void nestedSubscribe() { final AtomicInteger countChildren = new AtomicInteger(); final AtomicInteger countTotal = new AtomicInteger(); - final ArrayList list = new ArrayList(); + final ArrayList list = new ArrayList<>(); s.flatMap(new Function>() { @@ -263,7 +265,7 @@ public void reSubscribe() { final PublishSubject ps = PublishSubject.create(); Observer o1 = TestHelper.mockObserver(); - TestObserver to = new TestObserver(o1); + TestObserver to = new TestObserver<>(o1); ps.subscribe(to); // emit @@ -281,7 +283,7 @@ public void reSubscribe() { ps.onNext(2); Observer o2 = TestHelper.mockObserver(); - TestObserver to2 = new TestObserver(o2); + TestObserver to2 = new TestObserver<>(o2); ps.subscribe(to2); // emit @@ -392,7 +394,7 @@ public void currentStateMethodsError() { @Test public void crossCancel() { - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); TestObserver to2 = new TestObserver() { @Override public void onNext(Integer t) { @@ -414,8 +416,9 @@ public void onNext(Integer t) { } @Test + @SuppressUndeliverable public void crossCancelOnError() { - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); TestObserver to2 = new TestObserver() { @Override public void onError(Throwable t) { @@ -438,7 +441,7 @@ public void onError(Throwable t) { @Test public void crossCancelOnComplete() { - final TestObserver to1 = new TestObserver(); + final TestObserver to1 = new TestObserver<>(); TestObserver to2 = new TestObserver() { @Override public void onComplete() { @@ -572,7 +575,7 @@ public void addCompleteRance() throws Exception { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final PublishSubject ps = PublishSubject.create(); - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); Runnable r1 = new Runnable() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectBoundedConcurrencyTest.java b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectBoundedConcurrencyTest.java index 45520ebffa..419e5c92a9 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectBoundedConcurrencyTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectBoundedConcurrencyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,12 +19,12 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.observers.DefaultObserver; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -43,7 +43,7 @@ public void run() { @Override public void subscribe(Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); System.out.println("********* Start Source Data ***********"); for (long l = 1; l <= 10000; l++) { o.onNext(l); @@ -153,7 +153,7 @@ public void run() { @Override public void subscribe(Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); System.out.println("********* Start Source Data ***********"); for (long l = 1; l <= 10000; l++) { o.onNext(l); @@ -166,8 +166,8 @@ public void subscribe(Observer o) { }); // used to collect results of each thread - final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList>()); - final List threads = Collections.synchronizedList(new ArrayList()); + final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList<>()); + final List threads = Collections.synchronizedList(new ArrayList<>()); for (int i = 1; i <= 200; i++) { final int count = i; @@ -200,7 +200,7 @@ public void run() { } // assert all threads got the same results - List sums = new ArrayList(); + List sums = new ArrayList<>(); for (List values : listOfListsOfValues) { long v = 0; for (long l : values) { @@ -233,7 +233,7 @@ public void run() { public void subscribeCompletionRaceCondition() { for (int i = 0; i < 50; i++) { final ReplaySubject subject = ReplaySubject.createUnbounded(); - final AtomicReference value1 = new AtomicReference(); + final AtomicReference value1 = new AtomicReference<>(); subject.subscribe(new Consumer() { @@ -296,7 +296,7 @@ public void run() { public void raceForTerminalState() { final List expected = Arrays.asList(1); for (int i = 0; i < 100000; i++) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1).subscribeOn(Schedulers.computation()).cache().subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertValueSequence(expected); @@ -307,7 +307,7 @@ public void raceForTerminalState() { static class SubjectObserverThread extends Thread { private final ReplaySubject subject; - private final AtomicReference value = new AtomicReference(); + private final AtomicReference value = new AtomicReference<>(); SubjectObserverThread(ReplaySubject subject) { this.subject = subject; @@ -354,7 +354,7 @@ public void run() { } }); - final AtomicReference o = new AtomicReference(); + final AtomicReference o = new AtomicReference<>(); rs // .doOnSubscribe(v -> System.out.println("!! " + j)) diff --git a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectConcurrencyTest.java b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectConcurrencyTest.java index b0d2e55aad..454b56e515 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectConcurrencyTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectConcurrencyTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -19,12 +19,12 @@ import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; +import io.reactivex.rxjava3.disposables.Disposable; import org.junit.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposables; import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.observers.DefaultObserver; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -43,7 +43,7 @@ public void run() { @Override public void subscribe(Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); System.out.println("********* Start Source Data ***********"); for (long l = 1; l <= 10000; l++) { o.onNext(l); @@ -153,7 +153,7 @@ public void run() { @Override public void subscribe(Observer o) { - o.onSubscribe(Disposables.empty()); + o.onSubscribe(Disposable.empty()); System.out.println("********* Start Source Data ***********"); for (long l = 1; l <= 10000; l++) { o.onNext(l); @@ -166,8 +166,8 @@ public void subscribe(Observer o) { }); // used to collect results of each thread - final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList>()); - final List threads = Collections.synchronizedList(new ArrayList()); + final List> listOfListsOfValues = Collections.synchronizedList(new ArrayList<>()); + final List threads = Collections.synchronizedList(new ArrayList<>()); for (int i = 1; i <= 200; i++) { final int count = i; @@ -200,7 +200,7 @@ public void run() { } // assert all threads got the same results - List sums = new ArrayList(); + List sums = new ArrayList<>(); for (List values : listOfListsOfValues) { long v = 0; for (long l : values) { @@ -233,7 +233,7 @@ public void run() { public void subscribeCompletionRaceCondition() { for (int i = 0; i < 50; i++) { final ReplaySubject subject = ReplaySubject.create(); - final AtomicReference value1 = new AtomicReference(); + final AtomicReference value1 = new AtomicReference<>(); subject.subscribe(new Consumer() { @@ -296,7 +296,7 @@ public void run() { public void raceForTerminalState() { final List expected = Arrays.asList(1); for (int i = 0; i < 100000; i++) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1).subscribeOn(Schedulers.computation()).cache().subscribe(to); to.awaitDone(5, TimeUnit.SECONDS); to.assertValueSequence(expected); @@ -307,7 +307,7 @@ public void raceForTerminalState() { static class SubjectObserverThread extends Thread { private final ReplaySubject subject; - private final AtomicReference value = new AtomicReference(); + private final AtomicReference value = new AtomicReference<>(); SubjectObserverThread(ReplaySubject subject) { this.subject = subject; @@ -351,7 +351,7 @@ public void run() { } }); - final AtomicReference o = new AtomicReference(); + final AtomicReference o = new AtomicReference<>(); rs.subscribeOn(s).observeOn(Schedulers.io()) .subscribe(new DefaultObserver() { diff --git a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java index 1993112789..8417b53081 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/ReplaySubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -44,6 +44,7 @@ protected Subject create() { } @Test + @SuppressUndeliverable public void completed() { ReplaySubject subject = ReplaySubject.create(); @@ -68,13 +69,14 @@ public void completed() { } @Test + @SuppressUndeliverable public void completedStopsEmittingData() { ReplaySubject channel = ReplaySubject.create(); Observer observerA = TestHelper.mockObserver(); Observer observerB = TestHelper.mockObserver(); Observer observerC = TestHelper.mockObserver(); Observer observerD = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observerA); + TestObserver to = new TestObserver<>(observerA); channel.subscribe(to); channel.subscribe(observerB); @@ -137,6 +139,7 @@ public void completedStopsEmittingData() { } @Test + @SuppressUndeliverable public void completedAfterError() { ReplaySubject subject = ReplaySubject.create(); @@ -167,6 +170,7 @@ private void assertCompletedSubscriber(Observer observer) { } @Test + @SuppressUndeliverable public void error() { ReplaySubject subject = ReplaySubject.create(); @@ -225,7 +229,7 @@ public void unsubscribeFirstSubscriber() { ReplaySubject subject = ReplaySubject.create(); Observer observer = TestHelper.mockObserver(); - TestObserver to = new TestObserver(observer); + TestObserver to = new TestObserver<>(observer); subject.subscribe(to); subject.onNext("one"); @@ -256,7 +260,7 @@ private void assertObservedUntilTwo(Observer observer) { @Test public void newSubscriberDoesntBlockExisting() throws InterruptedException { - final AtomicReference lastValueForSubscriber1 = new AtomicReference(); + final AtomicReference lastValueForSubscriber1 = new AtomicReference<>(); Observer observer1 = new DefaultObserver() { @Override @@ -277,7 +281,7 @@ public void onNext(String v) { }; - final AtomicReference lastValueForSubscriber2 = new AtomicReference(); + final AtomicReference lastValueForSubscriber2 = new AtomicReference<>(); final CountDownLatch oneReceived = new CountDownLatch(1); final CountDownLatch makeSlow = new CountDownLatch(1); final CountDownLatch completed = new CountDownLatch(1); @@ -797,6 +801,7 @@ public void getValuesUnbounded() { } + @Test public void createInvalidCapacity() { try { ReplaySubject.create(-99); @@ -940,7 +945,7 @@ public void capacityHint() { @Test public void subscribeCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - final TestObserver to = new TestObserver(); + final TestObserver to = new TestObserver<>(); final ReplaySubject rp = ReplaySubject.create(); @@ -967,7 +972,7 @@ public void subscribeAfterDone() { ReplaySubject rp = ReplaySubject.create(); rp.onComplete(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); rp.onSubscribe(bs); @@ -1373,4 +1378,70 @@ public void timeAndSizeRemoveCorrectNumberOfOld() { rs.test().assertValuesOnly(4, 5); } + + @Test + public void terminationSubscriptionRaceUnbounded() throws Throwable { + for (int i = 1; i <= 10000; i++) { + Subject source = ReplaySubject.create(); + Subject sink = PublishSubject.create(); + TestObserver observer = sink.test(); + Schedulers.computation().scheduleDirect(() -> { + // issue signals to the source in adherence to the reactive streams specification + source.onSubscribe(Disposable.empty()); + source.onNext("hello"); + source.onNext("world"); + source.onComplete(); + }); + Schedulers.computation().scheduleDirect(() -> { + // connect the source to the sink in parallel with the signals issued to the source + // note the cast() operator, which is here to detect non-String escapees + source.cast(String.class).subscribe(sink); + }); + observer.await().assertValues("hello", "world").assertComplete(); + } + } + + @Test + public void terminationSubscriptionRaceSizeBound() throws Throwable { + for (int i = 1; i <= 10000; i++) { + Subject source = ReplaySubject.createWithSize(20); + Subject sink = PublishSubject.create(); + TestObserver observer = sink.test(); + Schedulers.computation().scheduleDirect(() -> { + // issue signals to the source in adherence to the reactive streams specification + source.onSubscribe(Disposable.empty()); + source.onNext("hello"); + source.onNext("world"); + source.onComplete(); + }); + Schedulers.computation().scheduleDirect(() -> { + // connect the source to the sink in parallel with the signals issued to the source + // note the cast() operator, which is here to detect non-String escapees + source.cast(String.class).subscribe(sink); + }); + observer.await().assertValues("hello", "world").assertComplete(); + } + } + + @Test + public void terminationSubscriptionRaceTimeBound() throws Throwable { + for (int i = 1; i <= 10000; i++) { + Subject source = ReplaySubject.createWithTime(20, TimeUnit.MINUTES, Schedulers.computation()); + Subject sink = PublishSubject.create(); + TestObserver observer = sink.test(); + Schedulers.computation().scheduleDirect(() -> { + // issue signals to the source in adherence to the reactive streams specification + source.onSubscribe(Disposable.empty()); + source.onNext("hello"); + source.onNext("world"); + source.onComplete(); + }); + Schedulers.computation().scheduleDirect(() -> { + // connect the source to the sink in parallel with the signals issued to the source + // note the cast() operator, which is here to detect non-String escapees + source.cast(String.class).subscribe(sink); + }); + observer.await().assertValues("hello", "world").assertComplete(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java index 9355d77d29..18ff503339 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/SerializedSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,9 +20,10 @@ import org.junit.Test; +import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.observers.TestObserver; import io.reactivex.rxjava3.plugins.RxJavaPlugins; @@ -32,8 +33,8 @@ public class SerializedSubjectTest extends RxJavaTest { @Test public void basic() { - SerializedSubject subject = new SerializedSubject(PublishSubject. create()); - TestObserver to = new TestObserver(); + SerializedSubject subject = new SerializedSubject<>(PublishSubject.create()); + TestObserver to = new TestObserver<>(); subject.subscribe(to); subject.onNext("hello"); subject.onComplete(); @@ -410,14 +411,14 @@ public void normal() { } s.onComplete(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); s.onSubscribe(bs); assertTrue(bs.isDisposed()); } @Test public void onNextOnNextRace() { - Set expectedSet = new HashSet(Arrays.asList(1, 2)); + Set expectedSet = new HashSet<>(Arrays.asList(1, 2)); for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final Subject s = PublishSubject.create().toSerialized(); @@ -446,7 +447,7 @@ public void run() { .assertValueCount(2) ; - Set actualSet = new HashSet(to.values()); + Set actualSet = new HashSet<>(to.values()); assertEquals("" + actualSet, expectedSet, actualSet); } } @@ -522,7 +523,7 @@ public void onNextOnSubscribeRace() { TestObserver to = s.test(); - final Disposable bs = Disposables.empty(); + final Disposable bs = Disposable.empty(); Runnable r1 = new Runnable() { @Override @@ -551,7 +552,7 @@ public void onCompleteOnSubscribeRace() { TestObserver to = s.test(); - final Disposable bs = Disposables.empty(); + final Disposable bs = Disposable.empty(); Runnable r1 = new Runnable() { @Override @@ -643,8 +644,8 @@ public void onSubscribeOnSubscribeRace() { TestObserver to = s.test(); - final Disposable bs1 = Disposables.empty(); - final Disposable bs2 = Disposables.empty(); + final Disposable bs1 = Disposable.empty(); + final Disposable bs2 = Disposable.empty(); Runnable r1 = new Runnable() { @Override @@ -665,4 +666,51 @@ public void run() { to.assertEmpty(); } } + + @Test + public void onErrorQueued() { + Subject sp = PublishSubject.create().toSerialized(); + + TestObserver to = new TestObserver() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + sp.onNext(2); + sp.onNext(3); + sp.onSubscribe(Disposable.empty()); + sp.onError(new TestException()); + } + } + }; + + sp.subscribe(to); + + sp.onNext(1); + + to.assertFailure(TestException.class, 1); // errors skip ahead + } + + @Test + public void onCompleteQueued() { + Subject sp = PublishSubject.create().toSerialized(); + + TestObserver to = new TestObserver() { + @Override + public void onNext(@NonNull Integer t) { + super.onNext(t); + if (t == 1) { + sp.onNext(2); + sp.onNext(3); + sp.onComplete(); + } + } + }; + + sp.subscribe(to); + + sp.onNext(1); + + to.assertResult(1, 2, 3); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subjects/SingleSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/SingleSubjectTest.java index 7c07b63f0a..751f0c72d4 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/SingleSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/SingleSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -209,7 +209,7 @@ public void onError(Throwable e) { public void onSubscribeDispose() { SingleSubject ss = SingleSubject.create(); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ss.onSubscribe(d); @@ -217,7 +217,7 @@ public void onSubscribeDispose() { ss.onSuccess(1); - d = Disposables.empty(); + d = Disposable.empty(); ss.onSubscribe(d); diff --git a/src/test/java/io/reactivex/rxjava3/subjects/SubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/SubjectTest.java index ab22d10a0c..ce7177a58d 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/SubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/SubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java b/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java index ba2deb2c91..8ae618319b 100644 --- a/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java +++ b/src/test/java/io/reactivex/rxjava3/subjects/UnicastSubjectTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,8 +26,8 @@ import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.testsupport.*; @@ -43,7 +43,7 @@ protected Subject create() { public void fusionLive() { UnicastSubject ap = UnicastSubject.create(); - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); ap.subscribe(to); @@ -68,7 +68,7 @@ public void fusionOfflie() { ap.onNext(1); ap.onComplete(); - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); ap.subscribe(to); @@ -124,7 +124,7 @@ public void fusionOfflineFailFast() { UnicastSubject ap = UnicastSubject.create(false); ap.onNext(1); ap.onError(new RuntimeException()); - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); ap.subscribe(to); to @@ -139,7 +139,7 @@ public void fusionOfflineFailFastMultipleEvents() { ap.onNext(2); ap.onNext(3); ap.onComplete(); - TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); ap.subscribe(to); to @@ -228,14 +228,14 @@ public void zeroCapacityHint() { public void completeCancelRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final int[] calls = { 0 }; - final UnicastSubject up = UnicastSubject.create(100, new Runnable() { + final UnicastSubject us = UnicastSubject.create(100, new Runnable() { @Override public void run() { calls[0]++; } }); - final TestObserver to = up.test(); + final TestObserver to = us.test(); Runnable r1 = new Runnable() { @Override @@ -247,7 +247,7 @@ public void run() { Runnable r2 = new Runnable() { @Override public void run() { - up.onComplete(); + us.onComplete(); } }; @@ -262,7 +262,7 @@ public void afterDone() { UnicastSubject p = UnicastSubject.create(); p.onComplete(); - Disposable bs = Disposables.empty(); + Disposable bs = Disposable.empty(); p.onSubscribe(bs); p.onNext(1); @@ -303,7 +303,7 @@ public void onErrorStatePeeking() { public void rejectSyncFusion() { UnicastSubject p = UnicastSubject.create(); - TestObserverEx to = new TestObserverEx(QueueFuseable.SYNC); + TestObserverEx to = new TestObserverEx<>(QueueFuseable.SYNC); p.subscribe(to); @@ -337,7 +337,7 @@ public void fusedDrainCancel() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final UnicastSubject p = UnicastSubject.create(); - final TestObserverEx to = new TestObserverEx(QueueFuseable.ANY); + final TestObserverEx to = new TestObserverEx<>(QueueFuseable.ANY); p.subscribe(to); @@ -363,10 +363,12 @@ public void run() { public void dispose() { final int[] calls = { 0 }; - UnicastSubject us = new UnicastSubject(128, new Runnable() { + UnicastSubject us = new UnicastSubject<>(128, new Runnable() { @Override - public void run() { calls[0]++; } - }); + public void run() { + calls[0]++; + } + }, true); TestHelper.checkDisposed(us); @@ -381,7 +383,7 @@ public void dispose() { RxJavaPlugins.reset(); } - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); us.onSubscribe(d); @@ -393,8 +395,8 @@ public void subscribeRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { final UnicastSubject us = UnicastSubject.create(); - final TestObserverEx to1 = new TestObserverEx(); - final TestObserverEx to2 = new TestObserverEx(); + final TestObserverEx to1 = new TestObserverEx<>(); + final TestObserverEx to2 = new TestObserverEx<>(); Runnable r1 = new Runnable() { @Override @@ -479,9 +481,7 @@ public void fusedNoConcurrentCleanDueToCancel() { us.onNext(i); } - to - .awaitDone(5, TimeUnit.SECONDS) - ; + to.awaitDone(10, TimeUnit.SECONDS); if (!errors.isEmpty()) { throw new CompositeException(errors); @@ -493,4 +493,20 @@ public void fusedNoConcurrentCleanDueToCancel() { } } } + + @Test + public void withCapacityHint() { + UnicastSubject us = UnicastSubject.create(16); + + TestObserver to = us.test(); + + for (int i = 0; i < 256; i++) { + us.onNext(i); + } + us.onComplete(); + + to.assertValueCount(256) + .assertComplete() + .assertNoErrors(); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/DefaultSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/DefaultSubscriberTest.java index ead17680fc..b68a6fe695 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/DefaultSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/DefaultSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public class DefaultSubscriberTest extends RxJavaTest { static final class RequestEarly extends DefaultSubscriber { - final List events = new ArrayList(); + final List events = new ArrayList<>(); RequestEarly() { request(5); diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/DisposableSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/DisposableSubscriberTest.java index 5958110afb..6aa5c1ff51 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/DisposableSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/DisposableSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -31,9 +31,9 @@ static final class TestDisposableSubscriber extends DisposableSubscriber { int start; - final List values = new ArrayList(); + final List values = new ArrayList<>(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int completions; @@ -62,7 +62,7 @@ public void onComplete() { @Test public void normal() { - TestDisposableSubscriber tc = new TestDisposableSubscriber(); + TestDisposableSubscriber tc = new TestDisposableSubscriber<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -83,7 +83,7 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestDisposableSubscriber tc = new TestDisposableSubscriber(); + TestDisposableSubscriber tc = new TestDisposableSubscriber<>(); tc.onSubscribe(new BooleanSubscription()); @@ -103,7 +103,7 @@ public void startOnce() { @Test public void dispose() { - TestDisposableSubscriber tc = new TestDisposableSubscriber(); + TestDisposableSubscriber tc = new TestDisposableSubscriber<>(); assertFalse(tc.isDisposed()); diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/ResourceSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/ResourceSubscriberTest.java index 190fe0e836..6bbc127cda 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/ResourceSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/ResourceSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,9 +30,9 @@ public class ResourceSubscriberTest extends RxJavaTest { static class TestResourceSubscriber extends ResourceSubscriber { - final List values = new ArrayList(); + final List values = new ArrayList<>(); - final List errors = new ArrayList(); + final List errors = new ArrayList<>(); int complete; @@ -71,17 +71,17 @@ void requestMore(long n) { @Test(expected = NullPointerException.class) public void nullResource() { - TestResourceSubscriber ro = new TestResourceSubscriber(); + TestResourceSubscriber ro = new TestResourceSubscriber<>(); ro.add(null); } @Test public void addResources() { - TestResourceSubscriber ro = new TestResourceSubscriber(); + TestResourceSubscriber ro = new TestResourceSubscriber<>(); assertFalse(ro.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ro.add(d); @@ -102,11 +102,11 @@ public void addResources() { @Test public void onCompleteCleansUp() { - TestResourceSubscriber ro = new TestResourceSubscriber(); + TestResourceSubscriber ro = new TestResourceSubscriber<>(); assertFalse(ro.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ro.add(d); @@ -121,11 +121,11 @@ public void onCompleteCleansUp() { @Test public void onErrorCleansUp() { - TestResourceSubscriber ro = new TestResourceSubscriber(); + TestResourceSubscriber ro = new TestResourceSubscriber<>(); assertFalse(ro.isDisposed()); - Disposable d = Disposables.empty(); + Disposable d = Disposable.empty(); ro.add(d); @@ -140,7 +140,7 @@ public void onErrorCleansUp() { @Test public void normal() { - TestResourceSubscriber tc = new TestResourceSubscriber(); + TestResourceSubscriber tc = new TestResourceSubscriber<>(); assertFalse(tc.isDisposed()); assertEquals(0, tc.start); @@ -161,7 +161,7 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestResourceSubscriber tc = new TestResourceSubscriber(); + TestResourceSubscriber tc = new TestResourceSubscriber<>(); tc.onSubscribe(new BooleanSubscription()); @@ -181,7 +181,7 @@ public void startOnce() { @Test public void dispose() { - TestResourceSubscriber tc = new TestResourceSubscriber(); + TestResourceSubscriber tc = new TestResourceSubscriber<>(); tc.dispose(); BooleanSubscription bs = new BooleanSubscription(); @@ -219,7 +219,7 @@ protected void onStart() { static final class RequestEarly extends ResourceSubscriber { - final List events = new ArrayList(); + final List events = new ArrayList<>(); RequestEarly() { request(5); diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java index 0dccdc1306..cd97fa11a3 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/SafeSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -41,7 +41,7 @@ public void onNextAfterOnError() { Flowable st = Flowable.unsafeCreate(t); Subscriber w = TestHelper.mockSubscriber(); - st.subscribe(new SafeSubscriber(new TestSubscriber(w))); + st.subscribe(new SafeSubscriber<>(new TestSubscriber<>(w))); t.sendOnNext("one"); t.sendOnError(new RuntimeException("bad")); @@ -62,7 +62,7 @@ public void onCompletedAfterOnError() { Subscriber w = TestHelper.mockSubscriber(); - st.subscribe(new SafeSubscriber(new TestSubscriber(w))); + st.subscribe(new SafeSubscriber<>(new TestSubscriber<>(w))); t.sendOnNext("one"); t.sendOnError(new RuntimeException("bad")); @@ -82,7 +82,7 @@ public void onNextAfterOnCompleted() { Flowable st = Flowable.unsafeCreate(t); Subscriber w = TestHelper.mockSubscriber(); - st.subscribe(new SafeSubscriber(new TestSubscriber(w))); + st.subscribe(new SafeSubscriber<>(new TestSubscriber<>(w))); t.sendOnNext("one"); t.sendOnCompleted(); @@ -98,12 +98,13 @@ public void onNextAfterOnCompleted() { * Ensure onError can not be called after onComplete. */ @Test + @SuppressUndeliverable public void onErrorAfterOnCompleted() { TestObservable t = new TestObservable(); Flowable st = Flowable.unsafeCreate(t); Subscriber w = TestHelper.mockSubscriber(); - st.subscribe(new SafeSubscriber(new TestSubscriber(w))); + st.subscribe(new SafeSubscriber<>(new TestSubscriber<>(w))); t.sendOnNext("one"); t.sendOnCompleted(); @@ -159,7 +160,7 @@ public void request(long n) { @Test public void onNextFailure() { - AtomicReference onError = new AtomicReference(); + AtomicReference onError = new AtomicReference<>(); try { OBSERVER_ONNEXT_FAIL(onError).onNext("one"); fail("expects exception to be thrown"); @@ -172,9 +173,9 @@ public void onNextFailure() { @Test public void onNextFailureSafe() { - AtomicReference onError = new AtomicReference(); + AtomicReference onError = new AtomicReference<>(); try { - SafeSubscriber safeObserver = new SafeSubscriber(OBSERVER_ONNEXT_FAIL(onError)); + SafeSubscriber safeObserver = new SafeSubscriber<>(OBSERVER_ONNEXT_FAIL(onError)); safeObserver.onSubscribe(new BooleanSubscription()); safeObserver.onNext("one"); assertNotNull(onError.get()); @@ -187,7 +188,7 @@ public void onNextFailureSafe() { @Test public void onCompleteFailure() { - AtomicReference onError = new AtomicReference(); + AtomicReference onError = new AtomicReference<>(); try { OBSERVER_ONCOMPLETED_FAIL(onError).onComplete(); fail("expects exception to be thrown"); @@ -341,16 +342,16 @@ public void onError(Throwable e) { public void onComplete() { } }; - SafeSubscriber s = new SafeSubscriber(actual); + SafeSubscriber s = new SafeSubscriber<>(actual); assertSame(actual, s.downstream); } @Test public void dispose() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -364,10 +365,11 @@ public void dispose() { } @Test + @SuppressUndeliverable public void onNextAfterComplete() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -386,9 +388,9 @@ public void onNextAfterComplete() { @Test public void onNextNull() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -401,9 +403,9 @@ public void onNextNull() { @Test public void onNextWithoutOnSubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); so.onNext(1); @@ -412,9 +414,9 @@ public void onNextWithoutOnSubscribe() { @Test public void onErrorWithoutOnSubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); so.onError(new TestException()); @@ -426,9 +428,9 @@ public void onErrorWithoutOnSubscribe() { @Test public void onCompleteWithoutOnSubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); so.onComplete(); @@ -437,9 +439,9 @@ public void onCompleteWithoutOnSubscribe() { @Test public void onNextNormal() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - SafeSubscriber so = new SafeSubscriber(ts); + SafeSubscriber so = new SafeSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -520,7 +522,7 @@ public void onComplete() { } public SafeSubscriber toSafe() { - return new SafeSubscriber(this); + return new SafeSubscriber<>(this); } public CrashDummy assertError(Class clazz) { diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java index 09b800d43e..2402762d93 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/SerializedSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ public void before() { } private Subscriber serializedSubscriber(Subscriber subscriber) { - return new SerializedSubscriber(subscriber); + return new SerializedSubscriber<>(subscriber); } @Test @@ -164,7 +164,7 @@ public void runOutOfOrderConcurrencyTest() { try { TestConcurrencySubscriber tw = new TestConcurrencySubscriber(); // we need Synchronized + SafeSubscriber to handle synchronization plus life-cycle - Subscriber w = serializedSubscriber(new SafeSubscriber(tw)); + Subscriber w = serializedSubscriber(new SafeSubscriber<>(tw)); Future f1 = tp.submit(new OnNextThread(w, 12000)); Future f2 = tp.submit(new OnNextThread(w, 5000)); @@ -220,7 +220,7 @@ public void runConcurrencyTest() { try { TestConcurrencySubscriber tw = new TestConcurrencySubscriber(); // we need Synchronized + SafeSubscriber to handle synchronization plus life-cycle - Subscriber w = serializedSubscriber(new SafeSubscriber(tw)); + Subscriber w = serializedSubscriber(new SafeSubscriber<>(tw)); w.onSubscribe(new BooleanSubscription()); Future f1 = tp.submit(new OnNextThread(w, 12000)); @@ -276,7 +276,7 @@ public void notificationDelay() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch running = new CountDownLatch(2); - TestSubscriberEx ts = new TestSubscriberEx(new DefaultSubscriber() { + TestSubscriberEx ts = new TestSubscriberEx<>(new DefaultSubscriber() { @Override public void onComplete() { @@ -341,11 +341,11 @@ public void onNext(String t) { * * When using SynchronizedSubscriber we get this output: * - * p1: 18 p2: 68 => should be close to each other unless we have thread starvation + * {@code p1: 18 p2: 68 =>} should be close to each other unless we have thread starvation * * When using SerializedSubscriber we get: * - * p1: 1 p2: 2445261 => should be close to each other unless we have thread starvation + * {@code p1: 1 p2: 2445261 =>} should be close to each other unless we have thread starvation * * This demonstrates how SynchronizedSubscriber balances back and forth better, and blocks emission. * The real issue in this example is the async buffer-bloat, so we need backpressure. @@ -357,7 +357,7 @@ public void onNext(String t) { @Test public void threadStarvation() throws InterruptedException { - TestSubscriber ts = new TestSubscriber(new DefaultSubscriber() { + TestSubscriber ts = new TestSubscriber<>(new DefaultSubscriber() { @Override public void onComplete() { @@ -553,7 +553,7 @@ private static class TestConcurrencySubscriber extends DefaultSubscriber /** * Used to store the order and number of events received. */ - private final LinkedBlockingQueue events = new LinkedBlockingQueue(); + private final LinkedBlockingQueue events = new LinkedBlockingQueue<>(); private final int waitTime; @SuppressWarnings("unused") @@ -849,7 +849,7 @@ protected void captureMaxThreads() { public void errorReentry() { List errors = TestHelper.trackPluginErrors(); try { - final AtomicReference> serial = new AtomicReference>(); + final AtomicReference> serial = new AtomicReference<>(); TestSubscriber ts = new TestSubscriber() { @Override @@ -859,7 +859,7 @@ public void onNext(Integer v) { super.onNext(v); } }; - SerializedSubscriber sobs = new SerializedSubscriber(ts); + SerializedSubscriber sobs = new SerializedSubscriber<>(ts); sobs.onSubscribe(new BooleanSubscription()); serial.set(sobs); @@ -876,7 +876,7 @@ public void onNext(Integer v) { @Test public void completeReentry() { - final AtomicReference> serial = new AtomicReference>(); + final AtomicReference> serial = new AtomicReference<>(); TestSubscriber ts = new TestSubscriber() { @Override @@ -886,7 +886,7 @@ public void onNext(Integer v) { super.onNext(v); } }; - SerializedSubscriber sobs = new SerializedSubscriber(ts); + SerializedSubscriber sobs = new SerializedSubscriber<>(ts); sobs.onSubscribe(new BooleanSubscription()); serial.set(sobs); @@ -899,9 +899,9 @@ public void onNext(Integer v) { @Test public void dispose() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - SerializedSubscriber so = new SerializedSubscriber(ts); + SerializedSubscriber so = new SerializedSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -915,9 +915,9 @@ public void dispose() { @Test public void onCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts); + final SerializedSubscriber so = new SerializedSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -941,9 +941,9 @@ public void run() { @Test public void onNextOnCompleteRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts); + final SerializedSubscriber so = new SerializedSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -977,9 +977,9 @@ public void run() { @Test public void onNextOnErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts); + final SerializedSubscriber so = new SerializedSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -1015,9 +1015,9 @@ public void run() { @Test public void onNextOnErrorRaceDelayError() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts, true); + final SerializedSubscriber so = new SerializedSubscriber<>(ts, true); BooleanSubscription bs = new BooleanSubscription(); @@ -1056,9 +1056,9 @@ public void startOnce() { List error = TestHelper.trackPluginErrors(); try { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts); + final SerializedSubscriber so = new SerializedSubscriber<>(ts); so.onSubscribe(new BooleanSubscription()); @@ -1079,9 +1079,9 @@ public void onCompleteOnErrorRace() { for (int i = 0; i < TestHelper.RACE_DEFAULT_LOOPS; i++) { List errors = TestHelper.trackPluginErrors(); try { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts); + final SerializedSubscriber so = new SerializedSubscriber<>(ts); BooleanSubscription bs = new BooleanSubscription(); @@ -1124,9 +1124,9 @@ public void run() { @Test public void nullOnNext() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); - final SerializedSubscriber so = new SerializedSubscriber(ts); + final SerializedSubscriber so = new SerializedSubscriber<>(ts); so.onSubscribe(new BooleanSubscription()); @@ -1134,4 +1134,29 @@ public void nullOnNext() { ts.assertFailureAndMessage(NullPointerException.class, ExceptionHelper.nullWarning("onNext called with a null value.")); } + + @Test + @SuppressUndeliverable + public void onErrorQueuedUp() { + AtomicReference> ssRef = new AtomicReference<>(); + TestSubscriberEx ts = new TestSubscriberEx() { + @Override + public void onNext(Integer t) { + super.onNext(t); + ssRef.get().onNext(2); + ssRef.get().onError(new TestException()); + } + }; + + final SerializedSubscriber so = new SerializedSubscriber<>(ts, true); + ssRef.set(so); + + BooleanSubscription bs = new BooleanSubscription(); + + so.onSubscribe(bs); + + so.onNext(1); + + ts.assertFailure(TestException.class, 1, 2); + } } diff --git a/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java b/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java index 33d95bea88..40bdcb95cc 100644 --- a/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java +++ b/src/test/java/io/reactivex/rxjava3/subscribers/TestSubscriberTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,8 +21,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.*; -import org.junit.rules.ExpectedException; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; import org.mockito.InOrder; import org.reactivestreams.*; @@ -38,13 +38,10 @@ public class TestSubscriberTest extends RxJavaTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void assertTestSubscriber() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); oi.subscribe(ts); ts.assertValues(1, 2); @@ -55,53 +52,47 @@ public void assertTestSubscriber() { @Test public void assertNotMatchCount() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriber ts = new TestSubscriber(); - oi.subscribe(ts); - - thrown.expect(AssertionError.class); - // FIXME different message pattern - // thrown.expectMessage("Number of items does not match. Provided: 1 Actual: 2"); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriber ts = new TestSubscriber<>(); + oi.subscribe(ts); - ts.assertValues(1); - ts.assertValueCount(2); - ts.assertComplete(); - ts.assertNoErrors(); + ts.assertValues(1); + ts.assertValueCount(2); + ts.assertComplete(); + ts.assertNoErrors(); + }); } @Test public void assertNotMatchValue() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriber ts = new TestSubscriber(); - oi.subscribe(ts); - - thrown.expect(AssertionError.class); - // FIXME different message pattern - // thrown.expectMessage("Value at index: 1 expected to be [3] (Integer) but was: [2] (Integer)"); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriber ts = new TestSubscriber<>(); + oi.subscribe(ts); - ts.assertValues(1, 3); - ts.assertValueCount(2); - ts.assertComplete(); - ts.assertNoErrors(); + ts.assertValues(1, 3); + ts.assertValueCount(2); + ts.assertComplete(); + ts.assertNoErrors(); + }); } @Test public void assertTerminalEventNotReceived() { - PublishProcessor p = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); - p.subscribe(ts); + assertThrows(AssertionError.class, () -> { + PublishProcessor p = PublishProcessor.create(); + TestSubscriber ts = new TestSubscriber<>(); + p.subscribe(ts); - p.onNext(1); - p.onNext(2); + p.onNext(1); + p.onNext(2); - thrown.expect(AssertionError.class); - // FIXME different message pattern - // thrown.expectMessage("No terminal events received."); - - ts.assertValues(1, 2); - ts.assertValueCount(2); - ts.assertComplete(); - ts.assertNoErrors(); + ts.assertValues(1, 2); + ts.assertValueCount(2); + ts.assertComplete(); + ts.assertNoErrors(); + }); } @Test @@ -109,7 +100,7 @@ public void wrappingMock() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); Subscriber mockSubscriber = TestHelper.mockSubscriber(); - oi.subscribe(new TestSubscriber(mockSubscriber)); + oi.subscribe(new TestSubscriber<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -122,7 +113,7 @@ public void wrappingMock() { public void wrappingMockWhenUnsubscribeInvolved() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)).take(2); Subscriber mockSubscriber = TestHelper.mockSubscriber(); - oi.subscribe(new TestSubscriber(mockSubscriber)); + oi.subscribe(new TestSubscriber<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -134,14 +125,14 @@ public void wrappingMockWhenUnsubscribeInvolved() { @Test public void assertError() { RuntimeException e = new RuntimeException("Oops"); - TestSubscriber subscriber = new TestSubscriber(); + TestSubscriber subscriber = new TestSubscriber<>(); Flowable.error(e).subscribe(subscriber); subscriber.assertError(e); } @Test public void awaitTerminalEventWithDuration() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).subscribe(ts); ts.awaitDone(1, TimeUnit.SECONDS); ts.assertComplete(); @@ -150,7 +141,7 @@ public void awaitTerminalEventWithDuration() { @Test public void awaitTerminalEventWithDurationAndUnsubscribeOnTimeout() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final AtomicBoolean unsub = new AtomicBoolean(false); Flowable.just(1) // @@ -169,28 +160,28 @@ public void run() { @Test(expected = NullPointerException.class) public void nullDelegate1() { - TestSubscriber ts = new TestSubscriber(null); + TestSubscriber ts = new TestSubscriber<>(null); ts.onComplete(); } @Test(expected = NullPointerException.class) public void nullDelegate2() { - TestSubscriber ts = new TestSubscriber(null); + TestSubscriber ts = new TestSubscriber<>(null); ts.onComplete(); } @Test(expected = NullPointerException.class) public void nullDelegate3() { - TestSubscriber ts = new TestSubscriber(null, 0L); + TestSubscriber ts = new TestSubscriber<>(null, 0L); ts.onComplete(); } @Test public void delegate1() { - TestSubscriber ts0 = new TestSubscriber(); + TestSubscriber ts0 = new TestSubscriber<>(); ts0.onSubscribe(EmptySubscription.INSTANCE); - TestSubscriber ts = new TestSubscriber(ts0); + TestSubscriber ts = new TestSubscriber<>(ts0); ts.onComplete(); ts0.assertComplete(); @@ -199,8 +190,8 @@ public void delegate1() { @Test public void delegate2() { - TestSubscriber ts1 = new TestSubscriber(); - TestSubscriber ts2 = new TestSubscriber(ts1); + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber<>(ts1); ts2.onComplete(); ts1.assertComplete(); @@ -208,21 +199,21 @@ public void delegate2() { @Test public void delegate3() { - TestSubscriber ts1 = new TestSubscriber(); - TestSubscriber ts2 = new TestSubscriber(ts1, 0L); + TestSubscriber ts1 = new TestSubscriber<>(); + TestSubscriber ts2 = new TestSubscriber<>(ts1, 0L); ts2.onComplete(); ts1.assertComplete(); } @Test public void unsubscribed() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); assertFalse(ts.isCancelled()); } @Test public void noErrors() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new TestException()); try { ts.assertNoErrors(); @@ -235,7 +226,7 @@ public void noErrors() { @Test public void notCompleted() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { ts.assertComplete(); } catch (AssertionError ex) { @@ -247,7 +238,7 @@ public void notCompleted() { @Test public void multipleCompletions() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onComplete(); ts.onComplete(); try { @@ -261,7 +252,7 @@ public void multipleCompletions() { @Test public void completed() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onComplete(); try { ts.assertNotComplete(); @@ -274,7 +265,7 @@ public void completed() { @Test public void multipleCompletions2() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onComplete(); ts.onComplete(); try { @@ -288,7 +279,7 @@ public void multipleCompletions2() { @Test public void multipleErrors() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -312,7 +303,7 @@ public void multipleErrors() { @Test public void multipleErrors2() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -333,7 +324,7 @@ public void multipleErrors2() { @Test public void multipleErrors3() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -354,7 +345,7 @@ public void multipleErrors3() { @Test public void multipleErrors4() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -375,7 +366,7 @@ public void multipleErrors4() { @Test public void differentError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new TestException()); try { ts.assertError(new TestException()); @@ -388,7 +379,7 @@ public void differentError() { @Test public void differentError2() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new RuntimeException()); try { ts.assertError(new TestException()); @@ -401,7 +392,7 @@ public void differentError2() { @Test public void differentError3() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new RuntimeException()); try { ts.assertError(TestException.class); @@ -415,7 +406,7 @@ public void differentError3() { @Test public void differentError4() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new RuntimeException()); try { ts.assertError(Functions.alwaysFalse()); @@ -428,7 +419,7 @@ public void differentError4() { @Test public void errorInPredicate() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new RuntimeException()); try { ts.assertError(new Predicate() { @@ -446,7 +437,7 @@ public boolean test(Throwable throwable) throws Exception { @Test public void noError() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { ts.assertError(TestException.class); } catch (AssertionError ex) { @@ -458,7 +449,7 @@ public void noError() { @Test public void noError2() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { ts.assertError(new TestException()); } catch (AssertionError ex) { @@ -470,7 +461,7 @@ public void noError2() { @Test public void noError3() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { ts.assertError(Functions.alwaysTrue()); } catch (AssertionError ex) { @@ -482,7 +473,7 @@ public void noError3() { @Test public void interruptTerminalEventAwait() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); @@ -508,7 +499,7 @@ public void run() { @Test public void interruptTerminalEventAwaitTimed() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); @@ -535,7 +526,7 @@ public void run() { @Test public void interruptTerminalEventAwaitAndUnsubscribe() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); @@ -563,7 +554,7 @@ public void run() { @Test public void noTerminalEventBut1Completed() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onComplete(); @@ -577,7 +568,7 @@ public void noTerminalEventBut1Completed() { @Test public void noTerminalEventBut1Error() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onError(new TestException()); @@ -591,7 +582,7 @@ public void noTerminalEventBut1Error() { @Test public void noTerminalEventBut1Error1Complete() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onComplete(); ts.onError(new TestException()); @@ -613,7 +604,7 @@ public void noTerminalEventBut1Error1Complete() { @Test public void noTerminalEventBut2Errors() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); @@ -635,7 +626,7 @@ public void noTerminalEventBut2Errors() { @Test public void noValues() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onNext(1); try { @@ -648,7 +639,7 @@ public void noValues() { @Test public void valueCount() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onNext(1); ts.onNext(2); @@ -668,7 +659,7 @@ public void onComplete() { throw new TestException(); } }; - TestSubscriber ts = new TestSubscriber(ts0); + TestSubscriber ts = new TestSubscriber<>(ts0); try { ts.onComplete(); @@ -687,7 +678,7 @@ public void onError(Throwable e) { throw new TestException(); } }; - TestSubscriber ts = new TestSubscriber(ts0); + TestSubscriber ts = new TestSubscriber<>(ts0); try { ts.onError(new RuntimeException()); @@ -1207,7 +1198,7 @@ public void assertValueSequence() { @Test public void assertEmpty() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { ts.assertEmpty(); @@ -1232,7 +1223,7 @@ public void assertEmpty() { @Test public void awaitDoneTimed() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Thread.currentThread().interrupt(); @@ -1245,7 +1236,7 @@ public void awaitDoneTimed() { @Test public void assertErrorMultiple() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); TestException e = new TestException(); ts.onError(e); @@ -1267,7 +1258,7 @@ public void assertErrorMultiple() { @Test public void assertComplete() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1294,7 +1285,7 @@ public void assertComplete() { @Test public void completeWithoutOnSubscribe() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.onComplete(); @@ -1303,7 +1294,7 @@ public void completeWithoutOnSubscribe() { @Test public void completeDelegateThrows() throws Exception { - TestSubscriber ts = new TestSubscriber(new FlowableSubscriber() { + TestSubscriber ts = new TestSubscriber<>(new FlowableSubscriber() { @Override public void onSubscribe(Subscription s) { @@ -1339,7 +1330,7 @@ public void onComplete() { @Test public void errorDelegateThrows() throws Exception { - TestSubscriber ts = new TestSubscriber(new FlowableSubscriber() { + TestSubscriber ts = new TestSubscriber<>(new FlowableSubscriber() { @Override public void onSubscribe(Subscription s) { @@ -1375,22 +1366,22 @@ public void onComplete() { @Test public void assertValuePredicateEmpty() { - TestSubscriber ts = new TestSubscriber(); + assertThrows(AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); - Flowable.empty().subscribe(ts); + Flowable.empty().subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - ts.assertValue(new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + ts.assertValue(new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValuePredicateMatch() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1).subscribe(ts); @@ -1401,54 +1392,58 @@ public void assertValuePredicateMatch() { }); } + static void assertThrowsWithMessage(String message, Class clazz, ThrowingRunnable run) { + assertEquals(message, assertThrows(clazz, run).getMessage()); + } + @Test public void assertValuePredicateNoMatch() { - TestSubscriber ts = new TestSubscriber(); + assertThrowsWithMessage("Value 1 (class: Integer) at position 0 did not pass the predicate (latch = 0, values = 1, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); - Flowable.just(1).subscribe(ts); + Flowable.just(1).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - ts.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 1; - } + ts.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 1; + } + }); }); } @Test public void assertValuePredicateMatchButMore() { - TestSubscriber ts = new TestSubscriber(); + assertThrowsWithMessage("The first value passed the predicate but this consumer received more than one value (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); - Flowable.just(1, 2).subscribe(ts); + Flowable.just(1, 2).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value present but other values as well"); - ts.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + ts.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertValueAtPredicateEmpty() { - TestSubscriber ts = new TestSubscriber(); + assertThrowsWithMessage("No values (latch = 0, values = 0, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); - Flowable.empty().subscribe(ts); + Flowable.empty().subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - ts.assertValueAt(0, new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + ts.assertValueAt(0, new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValueAtPredicateMatch() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); Flowable.just(1, 2).subscribe(ts); @@ -1461,31 +1456,68 @@ public void assertValueAtPredicateMatch() { @Test public void assertValueAtPredicateNoMatch() { - TestSubscriber ts = new TestSubscriber(); + assertThrowsWithMessage("Value 3 (class: Integer) at position 2 did not pass the predicate (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); - Flowable.just(1, 2, 3).subscribe(ts); + Flowable.just(1, 2, 3).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - ts.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 3; - } + ts.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 3; + } + }); }); } @Test public void assertValueAtInvalidIndex() { - TestSubscriber ts = new TestSubscriber(); + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); - Flowable.just(1, 2).subscribe(ts); + Flowable.just(1, 2).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)"); - ts.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + ts.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); + }); + } + + @Test + public void assertValueAtIndexInvalidIndex() { + assertThrowsWithMessage("Index 2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1, 2).subscribe(ts); + + ts.assertValueAt(2, 3); + }); + } + + @Test + public void assertValueAtIndexInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1, 2).subscribe(ts); + + ts.assertValueAt(-2, 3); + }); + } + + @Test + public void assertValueAtInvalidIndexNegative() { + assertThrowsWithMessage("Index -2 is out of range [0, 2) (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriber ts = new TestSubscriber<>(); + + Flowable.just(1, 2).subscribe(ts); + + ts.assertValueAt(-2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @@ -1563,7 +1595,7 @@ public void timeoutIndicated3() throws InterruptedException { @Test public void disposeIndicated() { - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); ts.cancel(); try { @@ -1671,4 +1703,40 @@ public void assertValuesOnlyThrowsWhenErrored() { // expected } } + + @Test + public void onErrorIsNull() { + TestSubscriber ts = TestSubscriber.create(); + ts.onSubscribe(new BooleanSubscription()); + + ts.onError(null); + + ts.assertFailure(NullPointerException.class); + } + + static final class TestSubscriberImpl extends TestSubscriber { + public boolean isTimeout() { + return timeout; + } + } + + @Test + public void awaitCountTimeout() { + TestSubscriberImpl ts = new TestSubscriberImpl<>(); + ts.onSubscribe(new BooleanSubscription()); + ts.awaitCount(1); + assertTrue(ts.isTimeout()); + } + + @Test(expected = RuntimeException.class) + public void awaitCountInterrupted() { + try { + TestSubscriber ts = TestSubscriber.create(); + ts.onSubscribe(new BooleanSubscription()); + Thread.currentThread().interrupt(); + ts.awaitCount(1); + } finally { + Thread.interrupted(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/tck/AllTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/AllTckTest.java index 5d2ce5eed9..4b8060fba9 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/AllTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/AllTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/AmbArrayTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/AmbArrayTckTest.java index 491eccb85b..1260dd2d58 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/AmbArrayTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/AmbArrayTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,6 @@ @Test public class AmbArrayTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/AmbTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/AmbTckTest.java index 2bdcf2c89f..6c687b7461 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/AmbTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/AmbTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,6 @@ @Test public class AmbTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/AnyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/AnyTckTest.java index 5d93dd5462..34e9fe5517 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/AnyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/AnyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/AsyncProcessorAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/AsyncProcessorAsPublisherTckTest.java index 87dd4afb33..87a75f0052 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/AsyncProcessorAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/AsyncProcessorAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java b/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java index d14aac229f..aa42bcc03a 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java +++ b/src/test/java/io/reactivex/rxjava3/tck/BaseTck.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -51,8 +51,8 @@ public long maxElementsFromPublisher() { /** * Creates an Iterable with the specified number of elements or an infinite one if - * elements > Integer.MAX_VALUE. - * @param elements the number of elements to return, Integer.MAX_VALUE means an infinite sequence + * {@code elements >} {@link Integer#MAX_VALUE}. + * @param elements the number of elements to return, {@link Integer#MAX_VALUE} means an infinite sequence * @return the Iterable */ protected Iterable iterate(long elements) { diff --git a/src/test/java/io/reactivex/rxjava3/tck/BehaviorProcessorAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/BehaviorProcessorAsPublisherTckTest.java index 219a8ab1a2..94c5e15c14 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/BehaviorProcessorAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/BehaviorProcessorAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/BufferBoundaryTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/BufferBoundaryTckTest.java index 18a6fe4a21..cf79b210d6 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/BufferBoundaryTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/BufferBoundaryTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/BufferExactSizeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/BufferExactSizeTckTest.java index 5af053e052..d4b7a7abf5 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/BufferExactSizeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/BufferExactSizeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/CacheTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CacheTckTest.java index 460fb80e70..ec9c481fdc 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CacheTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CacheTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/CollectTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CollectTckTest.java index fe09c2f061..76c359f327 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CollectTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CollectTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayDelayErrorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayDelayErrorTckTest.java index dcb5a94fe6..80bccac273 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayDelayErrorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayDelayErrorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public class CombineLatestArrayDelayErrorTckTest extends BaseTck { @Override public Publisher createPublisher(long elements) { return - Flowable.combineLatestDelayError( + Flowable.combineLatestArrayDelayError( new Publisher[] { Flowable.just(1L), Flowable.fromIterable(iterate(elements)) }, new Function() { @Override diff --git a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayTckTest.java index 2cebfe655b..c5a040dc25 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestArrayTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableDelayErrorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableDelayErrorTckTest.java index b309de7837..41883ab6c1 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableDelayErrorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableDelayErrorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ @Test public class CombineLatestIterableDelayErrorTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableTckTest.java index ed59b0aeb7..a7b9279a56 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CombineLatestIterableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ @Test public class CombineLatestIterableTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/CompletableAndThenPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CompletableAndThenPublisherTckTest.java index 9e9eae893d..22c1d4ceef 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CompletableAndThenPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CompletableAndThenPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatArrayEagerTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatArrayEagerTckTest.java index 7cf2fb3dc1..25afb45060 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatArrayEagerTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatArrayEagerTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,6 @@ @Test public class ConcatArrayEagerTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatIterableEagerTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatIterableEagerTckTest.java index 6ab7f2a7ba..491f211cc2 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatIterableEagerTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatIterableEagerTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,7 +21,6 @@ @Test public class ConcatIterableEagerTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapIterableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapIterableTckTest.java index 43dcb2cf20..1a5ab46193 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapIterableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapIterableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapMaybeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapMaybeTckTest.java index f01d8ec511..5216b6a50f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapMaybeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapMaybeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapSingleTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapSingleTckTest.java index 9259f0dcd9..5d41ff6775 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapSingleTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapSingleTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapTckTest.java index 7629d88742..83a3774193 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatMapTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatMapTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherEagerTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherEagerTckTest.java index 126fe4f01a..194d7e79de 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherEagerTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherEagerTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherTckTest.java index 4bbfe152f7..ad61abd029 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatTckTest.java index 1ad7d79412..c5eb360c73 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithCompletableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithCompletableTckTest.java index a1431efbf8..a5734952f5 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithCompletableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithCompletableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeEmptyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeEmptyTckTest.java index 81611f79e4..e333457b82 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeEmptyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeEmptyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeTckTest.java index 08ecaee5ac..b56ac86eac 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithMaybeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithSingleTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithSingleTckTest.java index 234ebdf419..56746e24fc 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ConcatWithSingleTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ConcatWithSingleTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/CreateTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/CreateTckTest.java index 45a2d86ad7..ff4b0597d8 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/CreateTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/CreateTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DefaultIfEmptyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DefaultIfEmptyTckTest.java index d8692cfc36..c9d9078bf5 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DefaultIfEmptyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DefaultIfEmptyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DeferTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DeferTckTest.java index cb75dbb290..f834c1e637 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DeferTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DeferTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DelaySubscriptionTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DelaySubscriptionTckTest.java index 28cc3243d7..5db6d0b877 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DelaySubscriptionTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DelaySubscriptionTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DelayTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DelayTckTest.java index d2d4c08de9..87ccaa37d2 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DelayTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DelayTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DistinctTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DistinctTckTest.java index 35fe953720..629d96fee6 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DistinctTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DistinctTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DistinctUntilChangedTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DistinctUntilChangedTckTest.java index 4c08351e46..ba69771a4f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DistinctUntilChangedTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DistinctUntilChangedTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DoAfterNextTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DoAfterNextTckTest.java index 1f988ee9f0..3b8fbebecb 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DoAfterNextTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DoAfterNextTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DoFinallyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DoFinallyTckTest.java index e59dc8ba7d..029892f440 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DoFinallyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DoFinallyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/DoOnNextTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/DoOnNextTckTest.java index 418535f8e4..f7e343803b 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/DoOnNextTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/DoOnNextTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ElementAtTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ElementAtTckTest.java index 1563beb090..58e18778d0 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ElementAtTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ElementAtTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/EmptyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/EmptyTckTest.java index 0b960f51b0..da65d54fe4 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/EmptyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/EmptyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FilterTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FilterTckTest.java index fc3cf46588..6c975dabfc 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FilterTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FilterTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FirstTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FirstTckTest.java index 5dac1a42f1..29082117dd 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FirstTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FirstTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FlatMapTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FlatMapTckTest.java index 39b9afc454..88e937f282 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FlatMapTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FlatMapTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FromArrayTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FromArrayTckTest.java index d8d88a1e68..84bcb0b70d 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FromArrayTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FromArrayTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FromCallableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FromCallableTckTest.java index e235109619..d786060933 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FromCallableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FromCallableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FromFutureTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FromFutureTckTest.java index ee0dd0695a..edff3c277b 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FromFutureTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FromFutureTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ public class FromFutureTckTest extends BaseTck { @Override public Publisher createPublisher(final long elements) { - FutureTask ft = new FutureTask(new Callable() { + FutureTask ft = new FutureTask<>(new Callable() { @Override public Long call() throws Exception { return 1L; diff --git a/src/test/java/io/reactivex/rxjava3/tck/FromIterableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FromIterableTckTest.java index 0418584039..94dc77c555 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FromIterableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FromIterableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/FromSupplierTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/FromSupplierTckTest.java index a19028deb8..acbe1df8b0 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/FromSupplierTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/FromSupplierTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/GenerateTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/GenerateTckTest.java index 97cbb4a811..b5c5ef30f6 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/GenerateTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/GenerateTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/GroupByTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/GroupByTckTest.java index 7d5f0503da..ebaedb86ae 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/GroupByTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/GroupByTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/HideTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/HideTckTest.java index 6fa26977c1..62c3e76c4e 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/HideTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/HideTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/IgnoreElementsTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/IgnoreElementsTckTest.java index 6a1848e88a..289e2e90eb 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/IgnoreElementsTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/IgnoreElementsTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/IntervalRangeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/IntervalRangeTckTest.java index 181823e981..b6c09d3d21 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/IntervalRangeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/IntervalRangeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/IntervalTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/IntervalTckTest.java index 05a25f6774..b9a01afc32 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/IntervalTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/IntervalTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/IsEmptyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/IsEmptyTckTest.java index 73cfb28550..a9ffce0294 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/IsEmptyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/IsEmptyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/JustTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/JustTckTest.java index 02805e5125..668b32a31a 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/JustTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/JustTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/LastTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/LastTckTest.java index facdb09c32..eb75e0342a 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/LastTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/LastTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/LimitTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/LimitTckTest.java index 3fe61b627a..08c526af03 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/LimitTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/LimitTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MapTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MapTckTest.java index 6c27b381d2..9875c412ac 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MapTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MapTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MaybeFlatMapPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MaybeFlatMapPublisherTckTest.java index 7b582b97c5..d23521b8ab 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MaybeFlatMapPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MaybeFlatMapPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergeIterableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergeIterableTckTest.java index f597b70905..38a105e2be 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergeIterableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergeIterableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -23,7 +23,6 @@ @Test public class MergeIterableTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergePublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergePublisherTckTest.java index 71241d03b1..42baefbf64 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergePublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergePublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergeTckTest.java index aaefe13fee..d0280ca83d 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergeWithCompletableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergeWithCompletableTckTest.java index 7345c157da..e7cd1a2aac 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergeWithCompletableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergeWithCompletableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeEmptyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeEmptyTckTest.java index cb7e919577..cd8bec5079 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeEmptyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeEmptyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeTckTest.java index d064babcdf..1da618c609 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergeWithMaybeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MergeWithSingleTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MergeWithSingleTckTest.java index cf0c894eae..0933253418 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MergeWithSingleTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MergeWithSingleTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorAsPublisherTckTest.java index 0c78050212..b4479181d3 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorRefCountedTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorRefCountedTckTest.java index 53b150ef5e..907d3e3f3f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorRefCountedTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorRefCountedTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorTckTest.java index a1089b7370..ac82f963f7 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/MulticastProcessorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public MulticastProcessorTckTest() { @Override public Processor createIdentityProcessor(int bufferSize) { MulticastProcessor mp = MulticastProcessor.create(); - return new RefCountProcessor(mp); + return new RefCountProcessor<>(mp); } @Override diff --git a/src/test/java/io/reactivex/rxjava3/tck/ObserveOnTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ObserveOnTckTest.java index 4b897c82be..d6e555be9c 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ObserveOnTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ObserveOnTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/OnBackpressureBufferTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/OnBackpressureBufferTckTest.java index 86839c926f..58053819aa 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/OnBackpressureBufferTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/OnBackpressureBufferTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/OnErrorResumeWithTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/OnErrorResumeWithTckTest.java index d70e27bcf2..4d239c9f4d 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/OnErrorResumeWithTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/OnErrorResumeWithTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/OnErrorReturnItemTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/OnErrorReturnItemTckTest.java index 708fe26829..abd7b28a4f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/OnErrorReturnItemTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/OnErrorReturnItemTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/PublishProcessorAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/PublishProcessorAsPublisherTckTest.java index 6154fd4b11..8e596b1995 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/PublishProcessorAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/PublishProcessorAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/PublishSelectorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/PublishSelectorTckTest.java index 85cc653825..f996fabe36 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/PublishSelectorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/PublishSelectorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/PublishTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/PublishTckTest.java index 8d436c2a06..b85684d1da 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/PublishTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/PublishTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/RangeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/RangeTckTest.java index 3cba4e0566..53f3ca180e 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/RangeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/RangeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/RebatchRequestsTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/RebatchRequestsTckTest.java index cca0025f76..c08682d237 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/RebatchRequestsTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/RebatchRequestsTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReduceTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReduceTckTest.java index 70253dcafb..9495021488 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReduceTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReduceTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReduceWithTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReduceWithTckTest.java index ba56f602c7..9a4f404982 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReduceWithTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReduceWithTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/RefCountProcessor.java b/src/test/java/io/reactivex/rxjava3/tck/RefCountProcessor.java index 908f3e010c..f7c62cf752 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/RefCountProcessor.java +++ b/src/test/java/io/reactivex/rxjava3/tck/RefCountProcessor.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -45,8 +45,8 @@ @SuppressWarnings("unchecked") RefCountProcessor(FlowableProcessor actual) { this.actual = actual; - this.upstream = new AtomicReference(); - this.subscribers = new AtomicReference[]>(EMPTY); + this.upstream = new AtomicReference<>(); + this.subscribers = new AtomicReference<>(EMPTY); } @Override @@ -75,7 +75,7 @@ public void onComplete() { @Override protected void subscribeActual(Subscriber s) { - RefCountSubscriber rcs = new RefCountSubscriber(s, this); + RefCountSubscriber rcs = new RefCountSubscriber<>(s, this); if (!add(rcs)) { EmptySubscription.error(new IllegalStateException("RefCountProcessor terminated"), s); return; diff --git a/src/test/java/io/reactivex/rxjava3/tck/RepeatTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/RepeatTckTest.java index 1af7ffa8ff..32f5832afd 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/RepeatTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/RepeatTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorSizeBoundAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorSizeBoundAsPublisherTckTest.java index 9608b57358..a32e49c6b9 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorSizeBoundAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorSizeBoundAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorTimeBoundAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorTimeBoundAsPublisherTckTest.java index 41e5529796..f53df02ae6 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorTimeBoundAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorTimeBoundAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorUnboundedAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorUnboundedAsPublisherTckTest.java index 3bc7682284..6f286d68de 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorUnboundedAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReplayProcessorUnboundedAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReplaySelectorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReplaySelectorTckTest.java index e944e57955..84b1a86d45 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReplaySelectorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReplaySelectorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ReplayTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ReplayTckTest.java index 4380e95746..f62ffb08f3 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ReplayTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ReplayTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/RetryTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/RetryTckTest.java index d2608c2dce..ba0a0f4b1f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/RetryTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/RetryTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ScanTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ScanTckTest.java index 231e9a2590..8a43120c02 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ScanTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ScanTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SequenceEqualTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SequenceEqualTckTest.java index 6c01635403..a4af22ed7e 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SequenceEqualTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SequenceEqualTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ShareTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ShareTckTest.java index 6e6b57e1b5..35c9b08e4b 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ShareTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ShareTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SingleFlatMapFlowableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SingleFlatMapFlowableTckTest.java index 653ad462c1..62565e6470 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SingleFlatMapFlowableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SingleFlatMapFlowableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SingleTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SingleTckTest.java index 10de09fba7..96af86f6bb 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SingleTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SingleTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SkipLastTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SkipLastTckTest.java index 9583bbe646..953986813f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SkipLastTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SkipLastTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SkipTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SkipTckTest.java index fcd248374e..4f29940485 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SkipTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SkipTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SkipUntilTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SkipUntilTckTest.java index 762ca1256b..976c4983f1 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SkipUntilTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SkipUntilTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SkipWhileTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SkipWhileTckTest.java index b560bb4694..e4995aedd8 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SkipWhileTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SkipWhileTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SortedTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SortedTckTest.java index c8132593e2..51ff134a9c 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SortedTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SortedTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SubscribeOnTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SubscribeOnTckTest.java index c38720d6d0..f4303d5605 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SubscribeOnTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SubscribeOnTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SwitchIfEmptyTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SwitchIfEmptyTckTest.java index 9ba97747ff..eb792e45f0 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SwitchIfEmptyTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SwitchIfEmptyTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SwitchMapDelayErrorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SwitchMapDelayErrorTckTest.java index cb27667aec..d9086e275d 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SwitchMapDelayErrorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SwitchMapDelayErrorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SwitchMapTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SwitchMapTckTest.java index 1d8ec8f800..1d41a6d11b 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SwitchMapTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SwitchMapTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/SwitchOnNextTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/SwitchOnNextTckTest.java index d275ca6766..196bf4f825 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/SwitchOnNextTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/SwitchOnNextTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TakeLastTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TakeLastTckTest.java index 594db93c9f..c737554363 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TakeLastTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TakeLastTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TakeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TakeTckTest.java index 1b2529c914..50fe18caf1 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TakeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TakeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TakeUntilTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TakeUntilTckTest.java index 256c01cc00..9695560615 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TakeUntilTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TakeUntilTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TakeWhileTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TakeWhileTckTest.java index aa2d32939b..24f6bd4d0d 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TakeWhileTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TakeWhileTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TimeIntervalTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TimeIntervalTckTest.java index d0c7cd52f4..3daebf143a 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TimeIntervalTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TimeIntervalTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TimeoutTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TimeoutTckTest.java index 52ad8d01aa..447a60e951 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TimeoutTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TimeoutTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TimerTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TimerTckTest.java index 5e280bd520..4a6eb00005 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TimerTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TimerTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/TimestampTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/TimestampTckTest.java index acdef0b9ee..48d34c8751 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/TimestampTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/TimestampTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ToListTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ToListTckTest.java index 7d7c11bf8c..e9d17f370f 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ToListTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ToListTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ToMapTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ToMapTckTest.java index 8fe4361189..ae27183ca0 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ToMapTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ToMapTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ToMultimapTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ToMultimapTckTest.java index 925f147a7e..c0d65cbd33 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ToMultimapTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ToMultimapTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ToSortedListTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ToSortedListTckTest.java index fed8ec3669..11d1029fc2 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ToSortedListTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ToSortedListTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorAsPublisherTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorAsPublisherTckTest.java index 142ffbd4a7..a888ce35fa 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorAsPublisherTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorAsPublisherTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorTckTest.java index f97b6ee83a..44bcc5a142 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/UnicastProcessorTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public UnicastProcessorTckTest() { @Override public Processor createIdentityProcessor(int bufferSize) { UnicastProcessor up = UnicastProcessor.create(); - return new RefCountProcessor(up); + return new RefCountProcessor<>(up); } @Override diff --git a/src/test/java/io/reactivex/rxjava3/tck/UnsubscribeOnTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/UnsubscribeOnTckTest.java index c3a386f387..1ab8398b74 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/UnsubscribeOnTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/UnsubscribeOnTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/UsingTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/UsingTckTest.java index 9c0e41f3d9..03460fdaba 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/UsingTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/UsingTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/WindowBoundaryTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/WindowBoundaryTckTest.java index bee09d0eb5..5b01ad696c 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/WindowBoundaryTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/WindowBoundaryTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/WindowExactSizeTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/WindowExactSizeTckTest.java index 5ee3500379..dae3bf0c45 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/WindowExactSizeTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/WindowExactSizeTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/WithLatestFromTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/WithLatestFromTckTest.java index 3d91048c37..ae7cddb7f9 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/WithLatestFromTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/WithLatestFromTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ZipIterableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ZipIterableTckTest.java index ec4583ff07..9dae4bcd9a 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ZipIterableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ZipIterableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -24,7 +24,6 @@ @Test public class ZipIterableTckTest extends BaseTck { - @SuppressWarnings("unchecked") @Override public Publisher createPublisher(long elements) { return diff --git a/src/test/java/io/reactivex/rxjava3/tck/ZipTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ZipTckTest.java index 4c94e97ed7..c9427876d8 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ZipTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ZipTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ZipWithIterableTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ZipWithIterableTckTest.java index 2d9eae43ea..6a4cde43f5 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ZipWithIterableTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ZipWithIterableTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/tck/ZipWithTckTest.java b/src/test/java/io/reactivex/rxjava3/tck/ZipWithTckTest.java index f71dd6b98d..6992605ad8 100644 --- a/src/test/java/io/reactivex/rxjava3/tck/ZipWithTckTest.java +++ b/src/test/java/io/reactivex/rxjava3/tck/ZipWithTckTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/BaseTestConsumerEx.java b/src/test/java/io/reactivex/rxjava3/testsupport/BaseTestConsumerEx.java index d997a065ba..47016f1fa5 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/BaseTestConsumerEx.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/BaseTestConsumerEx.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -16,10 +16,11 @@ import java.util.List; import io.reactivex.rxjava3.functions.Predicate; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.observers.BaseTestConsumer; +import io.reactivex.rxjava3.operators.QueueFuseable; + +import java.util.Objects; /** * Base class with shared infrastructure to support TestSubscriber and TestObserver. @@ -74,7 +75,7 @@ public final U assertNever(T value) { for (int i = 0; i < s; i++) { T v = this.values.get(i); - if (ObjectHelper.equals(v, value)) { + if (Objects.equals(v, value)) { throw fail("Value at position " + i + " is equal to " + valueAndClass(value) + "; Expected them to be different"); } } @@ -158,8 +159,9 @@ public final U assertErrorMessage(String message) { if (s == 1) { Throwable e = errors.get(0); String errorMessage = e.getMessage(); - if (!ObjectHelper.equals(message, errorMessage)) { - throw fail("Error message differs; exptected: " + message + " but was: " + errorMessage); + if (!Objects.equals(message, errorMessage)) { + throw fail("\nexpected: " + message + "\ngot: " + errorMessage + + "; Error message differs"); } } else { throw fail("Multiple errors"); @@ -176,6 +178,7 @@ public final U assertErrorMessage(String message) { * @param values the expected values, asserted in order * @return this */ + @SafeVarargs public final U assertFailure(Predicate errorPredicate, T... values) { return assertSubscribed() .assertValues(values) @@ -192,6 +195,7 @@ public final U assertFailure(Predicate errorPredicate, T... values) { * @param values the expected values, asserted in order * @return this */ + @SafeVarargs public final U assertFailureAndMessage(Class error, String message, T... values) { return assertSubscribed() diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java new file mode 100644 index 0000000000..4e701137ab --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverable.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.testsupport; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SuppressUndeliverable { +} diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java new file mode 100644 index 0000000000..28f6d2f89e --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/testsupport/SuppressUndeliverableRule.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.testsupport; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import io.reactivex.rxjava3.exceptions.UndeliverableException; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * A rule for suppressing UndeliverableException handling. + * + *

    Test classes that use this rule can suppress UndeliverableException + * handling by annotating the test method with SuppressUndeliverable. + */ +public class SuppressUndeliverableRule implements TestRule { + + static final class SuppressUndeliverableRuleStatement extends Statement { + private Statement base; + + SuppressUndeliverableRuleStatement(Statement base) { + this.base = base; + } + + @Override + public void evaluate() throws Throwable { + try { + RxJavaPlugins.setErrorHandler(throwable -> { + if (!(throwable instanceof UndeliverableException)) { + throwable.printStackTrace(); + Thread currentThread = Thread.currentThread(); + currentThread.getUncaughtExceptionHandler().uncaughtException(currentThread, throwable); + } + }); + base.evaluate(); + } finally { + RxJavaPlugins.setErrorHandler(null); + } + } + } + + @Override + public Statement apply(Statement base, Description description) { + if (description != null && description.getAnnotation(SuppressUndeliverable.class) != null) { + return new SuppressUndeliverableRuleStatement(base); + } else { + return base; + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java index 9037d203f6..d38ad8f998 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestHelper.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -30,20 +30,24 @@ import org.mockito.stubbing.Answer; import org.reactivestreams.*; +import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; -import io.reactivex.rxjava3.internal.functions.ObjectHelper; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.operators.completable.CompletableToFlowable; import io.reactivex.rxjava3.internal.operators.maybe.MaybeToFlowable; import io.reactivex.rxjava3.internal.operators.single.SingleToFlowable; -import io.reactivex.rxjava3.internal.subscriptions.BooleanSubscription; +import io.reactivex.rxjava3.internal.subscriptions.*; import io.reactivex.rxjava3.internal.util.ExceptionHelper; import io.reactivex.rxjava3.observers.BaseTestConsumer; +import io.reactivex.rxjava3.operators.ConditionalSubscriber; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; +import io.reactivex.rxjava3.operators.SimpleQueue; import io.reactivex.rxjava3.parallel.ParallelFlowable; import io.reactivex.rxjava3.plugins.RxJavaPlugins; import io.reactivex.rxjava3.processors.PublishProcessor; @@ -70,7 +74,7 @@ public enum TestHelper { public static final int RACE_LONG_LOOPS = 10000; /** - * Mocks a subscriber and prepares it to request Long.MAX_VALUE. + * Mocks a subscriber and prepares it to request {@link Long#MAX_VALUE}. * @param the value type * @return the mocked subscriber */ @@ -153,7 +157,7 @@ public static void checkUtilityClass(Class clazz) { } public static List trackPluginErrors() { - final List list = Collections.synchronizedList(new ArrayList()); + final List list = Collections.synchronizedList(new ArrayList<>()); RxJavaPlugins.setErrorHandler(new Consumer() { @Override @@ -196,7 +200,7 @@ public static void assertError(List list, int index, Class list, int index, Class source) { + List list = trackPluginErrors(); + try { + final CountDownLatch cdl = new CountDownLatch(1); + + FlowableSubscriber bad = new FlowableSubscriber() { + + @Override + public void onSubscribe(Subscription s) { + try { + s.request(-99); + s.cancel(); + s.cancel(); + } finally { + cdl.countDown(); + } + } + + @Override + public void onNext(Object t) { + + } + + @Override + public void onError(Throwable t) { + + } + + @Override + public void onComplete() { + + } + + }; + + @SuppressWarnings("unchecked") + FlowableSubscriber[] subs = new FlowableSubscriber[source.parallelism()]; + subs[0] = bad; + for (int i = 1; i < subs.length; i++) { + subs[i] = NoOpConsumer.INSTANCE; + } + source.subscribe(subs); + + try { + assertTrue(cdl.await(5, TimeUnit.SECONDS)); + } catch (InterruptedException ex) { + throw new AssertionError(ex.getMessage()); + } + + assertTrue(list.toString(), list.get(0) instanceof IllegalArgumentException); + assertEquals("n > 0 required but it was -99", list.get(0).getMessage()); + } finally { + RxJavaPlugins.setErrorHandler(null); + } + } + /** * Synchronizes the execution of two runnables (as much as possible) * to test race conditions. @@ -521,11 +587,11 @@ public static void doubleOnSubscribe(Subscriber subscriber) { public static void doubleOnSubscribe(Observer observer) { List errors = trackPluginErrors(); try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -547,11 +613,11 @@ public static void doubleOnSubscribe(Observer observer) { public static void doubleOnSubscribe(SingleObserver observer) { List errors = trackPluginErrors(); try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -573,11 +639,11 @@ public static void doubleOnSubscribe(SingleObserver observer) { public static void doubleOnSubscribe(CompletableObserver observer) { List errors = trackPluginErrors(); try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -599,11 +665,11 @@ public static void doubleOnSubscribe(CompletableObserver observer) { public static void doubleOnSubscribe(MaybeObserver observer) { List errors = trackPluginErrors(); try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -617,6 +683,18 @@ public static void doubleOnSubscribe(MaybeObserver observer) { } } + public static void checkDisposed(Disposable d) { + assertFalse("Disposed upfront?!", d.isDisposed()); + + d.dispose(); + + assertTrue("Not disposed?!", d.isDisposed()); + + d.dispose(); + + assertTrue("Not disposed again?!", d.isDisposed()); + } + /** * Checks if the upstream's Subscription sent through the onSubscribe reports * isCancelled properly before and after calling dispose. @@ -624,7 +702,7 @@ public static void doubleOnSubscribe(MaybeObserver observer) { * @param source the source to test */ public static void checkDisposed(Flowable source) { - final TestSubscriber ts = new TestSubscriber(0L); + final TestSubscriber ts = new TestSubscriber<>(0L); source.subscribe(new FlowableSubscriber() { @Override public void onSubscribe(Subscription s) { @@ -900,11 +978,11 @@ public static void checkDoubleOnSubscribeMaybe(Function, ? exten @Override protected void subscribeActual(MaybeObserver observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -954,11 +1032,11 @@ public static void checkDoubleOnSubscribeMaybeToSingle(Function, @Override protected void subscribeActual(MaybeObserver observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1008,11 +1086,11 @@ public static void checkDoubleOnSubscribeMaybeToObservable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1062,11 +1140,11 @@ public static void checkDoubleOnSubscribeMaybeToFlowable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1116,11 +1194,11 @@ public static void checkDoubleOnSubscribeSingleToMaybe(Function @Override protected void subscribeActual(SingleObserver observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1170,11 +1248,11 @@ public static void checkDoubleOnSubscribeSingleToObservable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1224,11 +1302,11 @@ public static void checkDoubleOnSubscribeSingleToFlowable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1277,11 +1355,11 @@ public static void checkDoubleOnSubscribeMaybeToCompletable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1331,11 +1409,11 @@ public static void checkDoubleOnSubscribeSingle(Function, ? ext @Override protected void subscribeActual(SingleObserver observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1422,6 +1500,134 @@ protected void subscribeActual(Subscriber subscriber) { } } + /** + * Check if the given transformed reactive type reports multiple onSubscribe calls to + * RxJavaPlugins. + * @param the input value type + * @param transform the transform to drive an operator + */ + @SuppressWarnings("unchecked") + public static void checkDoubleOnSubscribeParallel(Function, ? extends ParallelFlowable> transform) { + List errors = trackPluginErrors(); + try { + final Boolean[] b = { null, null, null, null }; + final CountDownLatch cdl = new CountDownLatch(2); + + ParallelFlowable source = new ParallelFlowable() { + @Override + public void subscribe(Subscriber[] subscribers) { + for (int i = 0; i < subscribers.length; i++) { + try { + BooleanSubscription bs1 = new BooleanSubscription(); + + subscribers[i].onSubscribe(bs1); + + BooleanSubscription bs2 = new BooleanSubscription(); + + subscribers[i].onSubscribe(bs2); + + b[i * 2 + 0] = bs1.isCancelled(); + b[i * 2 + 1] = bs2.isCancelled(); + } finally { + cdl.countDown(); + } + } + } + + @Override + public int parallelism() { + return 2; + } + }; + + ParallelFlowable out = transform.apply(source); + + out.subscribe(new Subscriber[] { NoOpConsumer.INSTANCE, NoOpConsumer.INSTANCE }); + + try { + assertTrue("Timed out", cdl.await(5, TimeUnit.SECONDS)); + } catch (InterruptedException ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } + + assertEquals("Rail 1 First disposed?", false, b[0]); + assertEquals("Rail 1 Second not disposed?", true, b[1]); + + assertEquals("Rail 2 First disposed?", false, b[2]); + assertEquals("Rail 2 Second not disposed?", true, b[3]); + + assertError(errors, 0, IllegalStateException.class, "Subscription already set!"); + assertError(errors, 1, IllegalStateException.class, "Subscription already set!"); + } catch (Throwable ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } finally { + RxJavaPlugins.reset(); + } + } + /** + * Check if the given transformed reactive type reports multiple onSubscribe calls to + * RxJavaPlugins. + * @param the input value type + * @param transform the transform to drive an operator + */ + public static void checkDoubleOnSubscribeParallelToFlowable(Function, ? extends Flowable> transform) { + List errors = trackPluginErrors(); + try { + final Boolean[] b = { null, null, null, null }; + final CountDownLatch cdl = new CountDownLatch(2); + + ParallelFlowable source = new ParallelFlowable() { + @Override + public void subscribe(Subscriber[] subscribers) { + for (int i = 0; i < subscribers.length; i++) { + try { + BooleanSubscription bs1 = new BooleanSubscription(); + + subscribers[i].onSubscribe(bs1); + + BooleanSubscription bs2 = new BooleanSubscription(); + + subscribers[i].onSubscribe(bs2); + + b[i * 2 + 0] = bs1.isCancelled(); + b[i * 2 + 1] = bs2.isCancelled(); + } finally { + cdl.countDown(); + } + } + } + + @Override + public int parallelism() { + return 2; + } + }; + + Flowable out = transform.apply(source); + + out.subscribe(NoOpConsumer.INSTANCE); + + try { + assertTrue("Timed out", cdl.await(5, TimeUnit.SECONDS)); + } catch (InterruptedException ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } + + assertEquals("Rail 1 First disposed?", false, b[0]); + assertEquals("Rail 1 Second not disposed?", true, b[1]); + + assertEquals("Rail 2 First disposed?", false, b[2]); + assertEquals("Rail 2 Second not disposed?", true, b[3]); + + assertError(errors, 0, IllegalStateException.class, "Subscription already set!"); + assertError(errors, 1, IllegalStateException.class, "Subscription already set!"); + } catch (Throwable ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } finally { + RxJavaPlugins.reset(); + } + } + /** * Check if the given transformed reactive type reports multiple onSubscribe calls to * RxJavaPlugins. @@ -1439,11 +1645,11 @@ public static void checkDoubleOnSubscribeObservable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1493,11 +1699,11 @@ public static void checkDoubleOnSubscribeObservableToSingle(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1547,11 +1753,11 @@ public static void checkDoubleOnSubscribeObservableToMaybe(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1600,11 +1806,11 @@ public static void checkDoubleOnSubscribeObservableToCompletable(Function observer) { try { - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); observer.onSubscribe(d1); - Disposable d2 = Disposables.empty(); + Disposable d2 = Disposable.empty(); observer.onSubscribe(d2); @@ -1867,11 +2073,11 @@ public static void checkDoubleOnSubscribeCompletable(Function void checkDoubleOnSubscribeCompletableToMaybe(Function void checkDoubleOnSubscribeCompletableToSingle(Function void checkDisposedMaybe(Function, ? extends MaybeSource> composer) { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { - new MaybeToFlowable(composer.apply(pp.singleElement())).subscribe(ts); + new MaybeToFlowable<>(composer.apply(pp.singleElement())).subscribe(ts); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -2145,7 +2351,7 @@ public static void checkDisposedMaybe(Function, ? extends MaybeS public static void checkDisposedCompletable(Function composer) { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); try { new CompletableToFlowable(composer.apply(pp.ignoreElements())).subscribe(ts); @@ -2169,10 +2375,34 @@ public static void checkDisposedCompletable(Function void checkDisposedMaybeToSingle(Function, ? extends SingleSource> composer) { PublishProcessor pp = PublishProcessor.create(); - TestSubscriber ts = new TestSubscriber(); + TestSubscriber ts = new TestSubscriber<>(); + + try { + new SingleToFlowable<>(composer.apply(pp.singleElement())).subscribe(ts); + } catch (Throwable ex) { + throw ExceptionHelper.wrapOrThrow(ex); + } + + assertTrue(pp.hasSubscribers()); + + ts.cancel(); + + assertFalse(pp.hasSubscribers()); + } + + /** + * Check if the operator applied to a Maybe source propagates dispose properly. + * @param the source value type + * @param the output value type + * @param composer the function to apply an operator to the provided Maybe source + */ + public static void checkDisposedSingleToMaybe(Function, ? extends MaybeSource> composer) { + PublishProcessor pp = PublishProcessor.create(); + + TestSubscriber ts = new TestSubscriber<>(); try { - new SingleToFlowable(composer.apply(pp.singleElement())).subscribe(ts); + new MaybeToFlowable<>(composer.apply(pp.singleOrError())).subscribe(ts); } catch (Throwable ex) { throw ExceptionHelper.wrapOrThrow(ex); } @@ -2190,6 +2420,7 @@ public static void checkDisposedMaybeToSingle(Function, ? extend * @param ts the TestSubscriber instance * @param classes the array of expected Throwables inside the Composite */ + @SafeVarargs public static void assertCompositeExceptions(TestSubscriberEx ts, Class... classes) { ts .assertSubscribed() @@ -2209,23 +2440,16 @@ public static void assertCompositeExceptions(TestSubscriberEx ts, Class ts, Object... classes) { + public static void assertCompositeExceptions(TestSubscriberEx ts, Object... classesAndMessages) { ts .assertSubscribed() .assertError(CompositeException.class) .assertNotComplete(); - List list = compositeList(ts.errors().get(0)); - - assertEquals(classes.length, list.size()); - - for (int i = 0; i < classes.length; i += 2) { - assertError(list, i, (Class)classes[i], (String)classes[i + 1]); - } + assertCompositeExceptionListOf(ts.errors().get(0), classesAndMessages); } /** @@ -2234,6 +2458,7 @@ public static void assertCompositeExceptions(TestSubscriberEx ts, Object... c * @param to the TestSubscriber instance * @param classes the array of expected Throwables inside the Composite */ + @SafeVarargs public static void assertCompositeExceptions(TestObserverEx to, Class... classes) { to .assertSubscribed() @@ -2253,22 +2478,26 @@ public static void assertCompositeExceptions(TestObserverEx to, Class to, Object... classes) { + public static void assertCompositeExceptions(TestObserverEx to, Object... classesAndMessages) { to .assertSubscribed() .assertError(CompositeException.class) .assertNotComplete(); - List list = compositeList(to.errors().get(0)); + assertCompositeExceptionListOf(to.errors().get(0), classesAndMessages); + } - assertEquals(classes.length, list.size()); + @SuppressWarnings("unchecked") + static void assertCompositeExceptionListOf(Throwable ex, Object... classesAndMessages) { + List list = compositeList(ex); + + assertEquals(classesAndMessages.length, 2 * list.size()); - for (int i = 0; i < classes.length; i += 2) { - assertError(list, i, (Class)classes[i], (String)classes[i + 1]); + for (int i = 0; i < list.size(); i++) { + assertError(list, i, (Class)classesAndMessages[2 * i], (String)classesAndMessages[2 * i + 1]); } } @@ -2278,6 +2507,7 @@ public static void assertCompositeExceptions(TestObserverEx to, Object... cla * @param p the target processor * @param values the values to emit */ + @SafeVarargs public static void emit(Processor p, T... values) { for (T v : values) { p.onNext(v); @@ -2291,6 +2521,7 @@ public static void emit(Processor p, T... values) { * @param p the target subject * @param values the values to emit */ + @SafeVarargs public static void emit(Subject p, T... values) { for (T v : values) { p.onNext(v); @@ -2466,7 +2697,7 @@ public static void checkBadSourceObservable(Function, Object> boolean once; @Override protected void subscribeActual(Observer observer) { - observer.onSubscribe(Disposables.empty()); + observer.onSubscribe(Disposable.empty()); if (once) { return; @@ -2495,7 +2726,7 @@ protected void subscribeActual(Observer observer) { if (o instanceof ObservableSource) { ObservableSource os = (ObservableSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2517,7 +2748,7 @@ protected void subscribeActual(Observer observer) { if (o instanceof Publisher) { Publisher os = (Publisher) o; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); os.subscribe(ts); @@ -2539,7 +2770,7 @@ protected void subscribeActual(Observer observer) { if (o instanceof SingleSource) { SingleSource os = (SingleSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2561,7 +2792,7 @@ protected void subscribeActual(Observer observer) { if (o instanceof MaybeSource) { MaybeSource os = (MaybeSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2583,7 +2814,7 @@ protected void subscribeActual(Observer observer) { if (o instanceof CompletableSource) { CompletableSource os = (CompletableSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2654,7 +2885,7 @@ protected void subscribeActual(Subscriber subscriber) { if (o instanceof ObservableSource) { ObservableSource os = (ObservableSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2676,7 +2907,7 @@ protected void subscribeActual(Subscriber subscriber) { if (o instanceof Publisher) { Publisher os = (Publisher) o; - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); os.subscribe(ts); @@ -2698,7 +2929,7 @@ protected void subscribeActual(Subscriber subscriber) { if (o instanceof SingleSource) { SingleSource os = (SingleSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2720,7 +2951,7 @@ protected void subscribeActual(Subscriber subscriber) { if (o instanceof MaybeSource) { MaybeSource os = (MaybeSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2742,7 +2973,7 @@ protected void subscribeActual(Subscriber subscriber) { if (o instanceof CompletableSource) { CompletableSource os = (CompletableSource) o; - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); os.subscribe(to); @@ -2778,7 +3009,7 @@ public static void checkInvalidParallelSubscribers(ParallelFlowable sourc @SuppressWarnings("unchecked") TestSubscriber[] tss = new TestSubscriber[n + 1]; for (int i = 0; i <= n; i++) { - tss[i] = new TestSubscriber().withTag("" + i); + tss[i] = new TestSubscriber<>().withTag("" + i); } source.subscribe(tss); @@ -2788,6 +3019,12 @@ public static void checkInvalidParallelSubscribers(ParallelFlowable sourc } } + /** + * Creates a fuseable Observable that does not emit anything but rejects + * fusion requests. + * @param the element type + * @return the new Observable + */ public static Observable rejectObservableFusion() { return new Observable() { @Override @@ -2836,6 +3073,12 @@ public boolean isDisposed() { }; } + /** + * Creates a fuseable Flowable that does not emit anything but rejects + * fusion requests. + * @param the element type + * @return the new Observable + */ public static Flowable rejectFlowableFusion() { return new Flowable() { @Override @@ -2893,12 +3136,12 @@ static final class FlowableStripBoundary extends Flowable implements Flowa @Override public Flowable apply(Flowable upstream) { - return new FlowableStripBoundary(upstream); + return new FlowableStripBoundary<>(upstream); } @Override protected void subscribeActual(Subscriber s) { - source.subscribe(new StripBoundarySubscriber(s)); + source.subscribe(new StripBoundarySubscriber<>(s)); } static final class StripBoundarySubscriber implements FlowableSubscriber, QueueSubscription { @@ -2984,8 +3227,19 @@ public void cancel() { } } + /** + * Strips the {@link QueueFuseable#BOUNDARY} mode flag when the downstream calls {@link QueueSubscription#requestFusion(int)}. + *

    + * By default, many operators use {@link QueueFuseable#BOUNDARY} to indicate upstream side-effects + * should not leak over a fused boundary. However, some tests want to verify if {@link QueueSubscription#poll()} crashes + * are handled correctly and the most convenient way is to crash {@link Flowable#map} that won't fuse with {@code BOUNDARY} + * flag. This transformer strips this flag and thus allows the function of {@code map} to be executed as part of the + * {@code poll()} chain. + * @param the element type of the flow + * @return the new Transformer instance + */ public static FlowableTransformer flowableStripBoundary() { - return new FlowableStripBoundary(null); + return new FlowableStripBoundary<>(null); } static final class ObservableStripBoundary extends Observable implements ObservableTransformer { @@ -2998,12 +3252,12 @@ static final class ObservableStripBoundary extends Observable implements O @Override public Observable apply(Observable upstream) { - return new ObservableStripBoundary(upstream); + return new ObservableStripBoundary<>(upstream); } @Override protected void subscribeActual(Observer observer) { - source.subscribe(new StripBoundaryObserver(observer)); + source.subscribe(new StripBoundaryObserver<>(observer)); } static final class StripBoundaryObserver implements Observer, QueueDisposable { @@ -3089,24 +3343,35 @@ public boolean isDisposed() { } } + /** + * Strips the {@link QueueFuseable#BOUNDARY} mode flag when the downstream calls {@link QueueDisposable#requestFusion(int)}. + *

    + * By default, many operators use {@link QueueFuseable#BOUNDARY} to indicate upstream side-effects + * should not leak over a fused boundary. However, some tests want to verify if {@link QueueDisposable#poll()} crashes + * are handled correctly and the most convenient way is to crash {@link Observable#map} that won't fuse with {@code BOUNDARY} + * flag. This transformer strips this flag and thus allows the function of {@code map} to be executed as part of the + * {@code poll()} chain. + * @param the element type of the flow + * @return the new Transformer instance + */ public static ObservableTransformer observableStripBoundary() { - return new ObservableStripBoundary(null); + return new ObservableStripBoundary<>(null); } public static TestConsumerExConverters testConsumer() { - return new TestConsumerExConverters(false, 0); + return new TestConsumerExConverters<>(false, 0); } public static TestConsumerExConverters testConsumer(boolean cancelled) { - return new TestConsumerExConverters(cancelled, 0); + return new TestConsumerExConverters<>(cancelled, 0); } public static TestConsumerExConverters testConsumer(final int fusionMode, final boolean cancelled) { - return new TestConsumerExConverters(cancelled, fusionMode); + return new TestConsumerExConverters<>(cancelled, fusionMode); } public static TestConsumerExConverters testConsumer(final boolean cancelled, final int fusionMode) { - return new TestConsumerExConverters(cancelled, fusionMode); + return new TestConsumerExConverters<>(cancelled, fusionMode); } public static FlowableConverter> testSubscriber(final long initialRequest) { @@ -3125,7 +3390,7 @@ public static FlowableConverter> testSubscriber(final return new FlowableConverter>() { @Override public TestSubscriberEx apply(Flowable f) { - TestSubscriberEx tse = new TestSubscriberEx(initialRequest); + TestSubscriberEx tse = new TestSubscriberEx<>(initialRequest); if (cancelled) { tse.cancel(); } @@ -3153,7 +3418,7 @@ public static final class TestConsumerExConverters implements @Override public TestObserverEx apply(Completable upstream) { - TestObserverEx toe = new TestObserverEx(); + TestObserverEx toe = new TestObserverEx<>(); if (cancelled) { toe.dispose(); } @@ -3163,7 +3428,7 @@ public TestObserverEx apply(Completable upstream) { @Override public TestObserverEx apply(Maybe upstream) { - TestObserverEx toe = new TestObserverEx(); + TestObserverEx toe = new TestObserverEx<>(); if (cancelled) { toe.dispose(); } @@ -3173,7 +3438,7 @@ public TestObserverEx apply(Maybe upstream) { @Override public TestObserverEx apply(Single upstream) { - TestObserverEx toe = new TestObserverEx(); + TestObserverEx toe = new TestObserverEx<>(); if (cancelled) { toe.dispose(); } @@ -3183,7 +3448,7 @@ public TestObserverEx apply(Single upstream) { @Override public TestObserverEx apply(Observable upstream) { - TestObserverEx toe = new TestObserverEx(); + TestObserverEx toe = new TestObserverEx<>(); if (cancelled) { toe.dispose(); } @@ -3193,7 +3458,7 @@ public TestObserverEx apply(Observable upstream) { @Override public TestSubscriberEx apply(Flowable upstream) { - TestSubscriberEx tse = new TestSubscriberEx(); + TestSubscriberEx tse = new TestSubscriberEx<>(); if (cancelled) { tse.dispose(); } @@ -3202,8 +3467,9 @@ public TestSubscriberEx apply(Flowable upstream) { } } + @SafeVarargs public static TestSubscriberEx assertValueSet(TestSubscriberEx ts, T... values) { - Set expectedSet = new HashSet(Arrays.asList(values)); + Set expectedSet = new HashSet<>(Arrays.asList(values)); for (T t : ts.values()) { if (!expectedSet.contains(t)) { throw ts.failWith("Item not in the set: " + BaseTestConsumer.valueAndClass(t)); @@ -3212,8 +3478,9 @@ public static TestSubscriberEx assertValueSet(TestSubscriberEx ts, T.. return ts; } + @SafeVarargs public static TestObserverEx assertValueSet(TestObserverEx to, T... values) { - Set expectedSet = new HashSet(Arrays.asList(values)); + Set expectedSet = new HashSet<>(Arrays.asList(values)); for (T t : to.values()) { if (!expectedSet.contains(t)) { throw to.failWith("Item not in the set: " + BaseTestConsumer.valueAndClass(t)); @@ -3230,31 +3497,54 @@ public static TestObserverEx assertValueSet(TestObserverEx to, T... va * @throws Exception on error */ public static File findSource(String baseClassName) throws Exception { + return findSource(baseClassName, "io.reactivex.rxjava3.core"); + } + + /** + * Given a base reactive type name, try to find its source in the current runtime + * path and return a file to it or null if not found. + * @param baseClassName the class name such as {@code Maybe} + * @param parentPackage the parent package such as {@code io.reactivex.rxjava3.core} + * @return the File pointing to the source + * @throws Exception on error + */ + public static File findSource(String baseClassName, String parentPackage) throws Exception { URL u = TestHelper.class.getResource(TestHelper.class.getSimpleName() + ".class"); String path = new File(u.toURI()).toString().replace('\\', '/'); + parentPackage = parentPackage.replace(".", "/"); // System.out.println(path); - int i = path.toLowerCase().indexOf("/rxjava"); - if (i < 0) { - System.out.println("Can't find the base RxJava directory"); - return null; - } + // Locate the src/main/java directory + String p = null; + while (true) { + int idx = path.lastIndexOf("/"); + if (idx < 0) { + break; + } + path = path.substring(0, idx); + String check = path + "/src/main/java"; - // find end of any potential postfix to /RxJava - int j = path.indexOf("/", i + 6); + if (new File(check).exists()) { + p = check + "/" + parentPackage + "/" + baseClassName + ".java"; + break; + } + } - String p = path.substring(0, j + 1) + "src/main/java/io/reactivex/rxjava3/core/" + baseClassName + ".java"; + if (p == null) { + System.err.println("Unable to locate the RxJava sources"); + return null; + } File f = new File(p); - if (!f.canRead()) { - System.out.println("Can't read " + p); - return null; + if (f.canRead()) { + return f; } - return f; + System.out.println("Can't read " + p); + return null; } /** @@ -3280,36 +3570,36 @@ public Integer apply(Integer v) throws Throwable { .to(transform); if (result instanceof MaybeSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((MaybeSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof SingleSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((SingleSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof CompletableSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((CompletableSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof ObservableSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((ObservableSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof Publisher) { - TestSubscriberEx ts = new TestSubscriberEx(); - disposable.set(Disposables.fromSubscription(ts)); + TestSubscriberEx ts = new TestSubscriberEx<>(); + disposable.set(Disposable.fromSubscription(ts)); ((Publisher)result) .subscribe(ts); @@ -3348,36 +3638,36 @@ public Integer apply(Integer v) throws Throwable { .to(transform); if (result instanceof MaybeSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((MaybeSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof SingleSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((SingleSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof CompletableSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((CompletableSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof ObservableSource) { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); disposable.set(to); ((ObservableSource)result) .subscribe(to); to.assertEmpty(); } else if (result instanceof Publisher) { - TestSubscriberEx ts = new TestSubscriberEx(); - disposable.set(Disposables.fromSubscription(ts)); + TestSubscriberEx ts = new TestSubscriberEx<>(); + disposable.set(Disposable.fromSubscription(ts)); ((Publisher)result) .subscribe(ts); @@ -3419,4 +3709,155 @@ public static long awaitGC(long oneSleep, int maxLoop, long expectedMemoryUsage) } return bean.getHeapMemoryUsage().getUsed(); } + + /** + * Enable thracking of the global errors for the duration of the action. + * @param action the action to run with a list of errors encountered + * @throws Throwable the exception rethrown from the action + */ + public static void withErrorTracking(Consumer> action) throws Throwable { + List errors = trackPluginErrors(); + try { + action.accept(errors); + } finally { + RxJavaPlugins.reset(); + } + } + + /** + * Assert if the given CompletableFuture fails with a specified error inside an ExecutionException. + * @param cf the CompletableFuture to test + * @param error the error class expected + */ + public static void assertError(CompletableFuture cf, Class error) { + try { + cf.get(); + fail("Should have thrown!"); + } catch (Throwable ex) { + if (!error.isInstance(ex.getCause())) { + ex.printStackTrace(); + fail("Wrong cause: " + ex.getCause()); + } + } + } + + /** + * Syncs the execution of the given runnable with the execution of the + * current thread. + * @param run the other task to run in sync with the current thread + * @param resume the latch to count down after the {@code run} + */ + public static void raceOther(Runnable run, CountDownLatch resume) { + AtomicInteger sync = new AtomicInteger(2); + + Schedulers.single().scheduleDirect(() -> { + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + + run.run(); + + resume.countDown(); + }); + + if (sync.decrementAndGet() != 0) { + while (sync.get() != 0) { } + } + } + + /** + * Inserts a ConditionalSubscriber into the chain to trigger the conditional paths + * without interfering with the requestFusion parts. + * @param the element type + * @return the new FlowableTransformer instance + */ + public static FlowableTransformer conditional() { + return f -> new Flowable() { + @Override + protected void subscribeActual(@NonNull Subscriber<@NonNull ? super T> subscriber) { + f.subscribe(new ForwardingConditionalSubscriber<>(subscriber)); + } + }; + } + + /** + * Wraps a Subscriber and exposes it as a fuseable conditional subscriber without interfering with + * requestFusion. + * @param the element type + */ + static final class ForwardingConditionalSubscriber extends BasicQueueSubscription implements ConditionalSubscriber { + + private static final long serialVersionUID = 365317603608134078L; + + final Subscriber downstream; + + Subscription upstream; + + QueueSubscription qs; + + ForwardingConditionalSubscriber(Subscriber downstream) { + this.downstream = downstream; + } + + @SuppressWarnings("unchecked") + @Override + public void onSubscribe(@NonNull Subscription s) { + this.upstream = s; + if (s instanceof QueueSubscription) { + this.qs = (QueueSubscription)s; + } + downstream.onSubscribe(this); + } + + @Override + public void onNext(@NonNull T t) { + downstream.onNext(t); + } + + @Override + public boolean tryOnNext(@NonNull T t) { + downstream.onNext(t); + return true; + } + + @Override + public void onError(Throwable t) { + downstream.onError(t); + } + + @Override + public void onComplete() { + downstream.onComplete(); + } + + @Override + public int requestFusion(int mode) { + return qs != null ? qs.requestFusion(mode) : 0; + } + + @Override + public @Nullable T poll() throws Throwable { + return qs.poll(); + } + + @Override + public boolean isEmpty() { + return qs.isEmpty(); + } + + @Override + public void clear() { + qs.clear(); + } + + @Override + public void request(long n) { + upstream.request(n); + } + + @Override + public void cancel() { + upstream.cancel(); + } + } } diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverEx.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverEx.java index 9747d034bb..f029ce5457 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverEx.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverEx.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.testsupport; import java.util.concurrent.atomic.AtomicReference; @@ -17,7 +18,8 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.fuseable.*; +import io.reactivex.rxjava3.operators.QueueDisposable; +import io.reactivex.rxjava3.operators.QueueFuseable; /** * An extended test Observer that records events and allows making assertions about them. @@ -36,7 +38,7 @@ public class TestObserverEx private final Observer downstream; /** Holds the current subscription if any. */ - private final AtomicReference upstream = new AtomicReference(); + private final AtomicReference upstream = new AtomicReference<>(); private QueueDisposable qd; @@ -252,8 +254,8 @@ public final TestObserverEx assertFusionMode(int mode) { int m = establishedFusionMode; if (m != mode) { if (qd != null) { - throw new AssertionError("Fusion mode different. Expected: " + fusionModeToString(mode) - + ", actual: " + fusionModeToString(m)); + throw new AssertionError("\nexpected: " + fusionModeToString(mode) + + "\ngot: " + fusionModeToString(m) + "; Fusion mode different"); } else { throw fail("Upstream is not fuseable"); } diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverExTest.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverExTest.java index 6f8d3e25cc..2296ceb757 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverExTest.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestObserverExTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -20,32 +20,28 @@ import java.util.*; import java.util.concurrent.TimeUnit; -import org.junit.*; -import org.junit.rules.ExpectedException; +import org.junit.Test; import org.mockito.InOrder; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.core.RxJavaTest; -import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.operators.observable.ObservableScalarXMap.ScalarDisposable; import io.reactivex.rxjava3.observers.TestObserver; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.*; public class TestObserverExTest extends RxJavaTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void assertTestObserverEx() { Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); - TestObserverEx subscriber = new TestObserverEx(); + TestObserverEx subscriber = new TestObserverEx<>(); oi.subscribe(subscriber); subscriber.assertValues(1, 2); @@ -55,38 +51,34 @@ public void assertTestObserverEx() { @Test public void assertNotMatchCount() { - Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); - TestObserverEx subscriber = new TestObserverEx(); - oi.subscribe(subscriber); - - thrown.expect(AssertionError.class); - // FIXME different message format -// thrown.expectMessage("Number of items does not match. Provided: 1 Actual: 2"); - - subscriber.assertValue(1); - subscriber.assertValueCount(2); - subscriber.assertTerminated(); + assertThrows(AssertionError.class, () -> { + Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); + TestObserverEx subscriber = new TestObserverEx<>(); + oi.subscribe(subscriber); + + subscriber.assertValue(1); + subscriber.assertValueCount(2); + subscriber.assertTerminated(); + }); } @Test public void assertNotMatchValue() { - Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); - TestObserverEx subscriber = new TestObserverEx(); - oi.subscribe(subscriber); - - thrown.expect(AssertionError.class); - // FIXME different message format -// thrown.expectMessage("Value at index: 1 expected to be [3] (Integer) but was: [2] (Integer)"); - - subscriber.assertValues(1, 3); - subscriber.assertValueCount(2); - subscriber.assertTerminated(); + assertThrows(AssertionError.class, () -> { + Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); + TestObserverEx subscriber = new TestObserverEx<>(); + oi.subscribe(subscriber); + + subscriber.assertValues(1, 3); + subscriber.assertValueCount(2); + subscriber.assertTerminated(); + }); } @Test public void assertNeverAtNotMatchingValue() { Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); - TestObserverEx subscriber = new TestObserverEx(); + TestObserverEx subscriber = new TestObserverEx<>(); oi.subscribe(subscriber); subscriber.assertNever(3); @@ -96,40 +88,40 @@ public void assertNeverAtNotMatchingValue() { @Test public void assertNeverAtMatchingValue() { - Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); - TestObserverEx subscriber = new TestObserverEx(); - oi.subscribe(subscriber); + assertThrows(AssertionError.class, () -> { + Observable oi = Observable.fromIterable(Arrays.asList(1, 2)); + TestObserverEx subscriber = new TestObserverEx<>(); + oi.subscribe(subscriber); - subscriber.assertValues(1, 2); + subscriber.assertValues(1, 2); - thrown.expect(AssertionError.class); - - subscriber.assertNever(2); - subscriber.assertValueCount(2); - subscriber.assertTerminated(); + subscriber.assertNever(2); + subscriber.assertValueCount(2); + subscriber.assertTerminated(); + }); } @Test public void assertNeverAtMatchingPredicate() { - TestObserverEx to = new TestObserverEx(); + assertThrows(AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just(1, 2).subscribe(to); + Observable.just(1, 2).subscribe(to); - to.assertValues(1, 2); + to.assertValues(1, 2); - thrown.expect(AssertionError.class); - - to.assertNever(new Predicate() { - @Override - public boolean test(final Integer o) throws Exception { - return o == 1; - } + to.assertNever(new Predicate() { + @Override + public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertNeverAtNotMatchingPredicate() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(2, 3).subscribe(to); @@ -143,20 +135,18 @@ public boolean test(final Integer o) throws Exception { @Test public void assertTerminalEventNotReceived() { - PublishSubject p = PublishSubject.create(); - TestObserverEx subscriber = new TestObserverEx(); - p.subscribe(subscriber); - - p.onNext(1); - p.onNext(2); + assertThrows(AssertionError.class, () -> { + PublishSubject p = PublishSubject.create(); + TestObserverEx subscriber = new TestObserverEx<>(); + p.subscribe(subscriber); - thrown.expect(AssertionError.class); - // FIXME different message format -// thrown.expectMessage("No terminal events received."); + p.onNext(1); + p.onNext(2); - subscriber.assertValues(1, 2); - subscriber.assertValueCount(2); - subscriber.assertTerminated(); + subscriber.assertValues(1, 2); + subscriber.assertValueCount(2); + subscriber.assertTerminated(); + }); } @Test @@ -165,7 +155,7 @@ public void wrappingMock() { Observer mockSubscriber = TestHelper.mockObserver(); - oi.subscribe(new TestObserverEx(mockSubscriber)); + oi.subscribe(new TestObserverEx<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -178,7 +168,7 @@ public void wrappingMock() { public void wrappingMockWhenUnsubscribeInvolved() { Observable oi = Observable.fromIterable(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)).take(2); Observer mockSubscriber = TestHelper.mockObserver(); - oi.subscribe(new TestObserverEx(mockSubscriber)); + oi.subscribe(new TestObserverEx<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -189,12 +179,12 @@ public void wrappingMockWhenUnsubscribeInvolved() { @Test public void errorSwallowed() { - Observable.error(new RuntimeException()).subscribe(new TestObserverEx()); + Observable.error(new RuntimeException()).subscribe(new TestObserverEx<>()); } @Test public void nullExpected() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onNext(1); try { @@ -208,7 +198,7 @@ public void nullExpected() { @Test public void nullActual() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onNext(null); try { @@ -222,7 +212,7 @@ public void nullActual() { @Test public void terminalErrorOnce() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onError(new TestException()); to.onError(new TestException()); @@ -237,7 +227,7 @@ public void terminalErrorOnce() { @Test public void terminalCompletedOnce() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onComplete(); to.onComplete(); @@ -252,7 +242,7 @@ public void terminalCompletedOnce() { @Test public void terminalOneKind() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onError(new TestException()); to.onComplete(); @@ -267,15 +257,15 @@ public void terminalOneKind() { @Test public void createDelegate() { - TestObserverEx to1 = new TestObserverEx(); + TestObserverEx to1 = new TestObserverEx<>(); - TestObserverEx to = new TestObserverEx(to1); + TestObserverEx to = new TestObserverEx<>(to1); to.assertNotSubscribed(); assertFalse(to.hasSubscription()); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.assertNotSubscribed(); @@ -326,7 +316,7 @@ public void createDelegate() { @Test public void assertError() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); try { to.assertError(TestException.class); @@ -370,7 +360,7 @@ public void assertError() { // expected } - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertSubscribed(); @@ -451,9 +441,9 @@ public void valueAndClass() { @Test public void assertFailure() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onError(new TestException("Forced failure")); @@ -474,9 +464,9 @@ public void assertFailure() { @Test public void assertFuseable() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertNotFuseable(); @@ -494,10 +484,10 @@ public void assertFuseable() { // expected } - to = new TestObserverEx(); + to = new TestObserverEx<>(); to.setInitialFusionMode(QueueFuseable.ANY); - to.onSubscribe(new ScalarDisposable(to, 1)); + to.onSubscribe(new ScalarDisposable<>(to, 1)); to.assertFuseable(); @@ -521,7 +511,7 @@ public void assertFuseable() { @Test public void assertTerminated() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.assertNotTerminated(); @@ -537,9 +527,9 @@ public void assertTerminated() { @Test public void assertResult() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onComplete(); @@ -574,9 +564,9 @@ public void assertResult() { @Test public void await() throws Exception { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); assertFalse(to.await(100, TimeUnit.MILLISECONDS)); @@ -596,9 +586,9 @@ public void await() throws Exception { to.assertNoErrors().assertComplete(); - final TestObserverEx to1 = new TestObserverEx(); + final TestObserverEx to1 = new TestObserverEx<>(); - to1.onSubscribe(Disposables.empty()); + to1.onSubscribe(Disposable.empty()); Schedulers.single().scheduleDirect(new Runnable() { @Override @@ -612,9 +602,9 @@ public void run() { @Test public void errors() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); assertEquals(0, to.errors().size()); @@ -627,9 +617,9 @@ public void errors() { @Test public void onNext() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertValueCount(0); @@ -658,9 +648,9 @@ public void fusionModeToString() { @Test public void multipleTerminals() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertNotComplete(); @@ -701,9 +691,9 @@ public void multipleTerminals() { @Test public void assertValue() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.assertValue(1); @@ -735,15 +725,15 @@ public void assertValue() { @Test public void onNextMisbehave() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onNext(1); to.assertError(IllegalStateException.class); - to = new TestObserverEx(); + to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onNext(null); @@ -752,9 +742,9 @@ public void onNextMisbehave() { @Test public void assertTerminated2() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertNotTerminated(); @@ -778,9 +768,9 @@ public void assertTerminated2() { // expected } - to = new TestObserverEx(); + to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onError(new TestException()); to.onComplete(); @@ -795,17 +785,17 @@ public void assertTerminated2() { @Test public void onSubscribe() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onSubscribe(null); to.assertError(NullPointerException.class); - to = new TestObserverEx(); + to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); - Disposable d1 = Disposables.empty(); + Disposable d1 = Disposable.empty(); to.onSubscribe(d1); @@ -813,10 +803,10 @@ public void onSubscribe() { to.assertError(IllegalStateException.class); - to = new TestObserverEx(); + to = new TestObserverEx<>(); to.dispose(); - d1 = Disposables.empty(); + d1 = Disposable.empty(); to.onSubscribe(d1); @@ -826,9 +816,9 @@ public void onSubscribe() { @Test public void assertValueSequence() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.onNext(1); to.onNext(2); @@ -859,7 +849,7 @@ public void assertValueSequence() { @Test public void assertEmpty() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); try { to.assertEmpty(); @@ -868,7 +858,7 @@ public void assertEmpty() { // expected } - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); to.assertEmpty(); @@ -884,7 +874,7 @@ public void assertEmpty() { @Test public void awaitDoneTimed() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Thread.currentThread().interrupt(); @@ -897,7 +887,7 @@ public void awaitDoneTimed() { @Test public void assertNotSubscribed() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.assertNotSubscribed(); @@ -913,7 +903,7 @@ public void assertNotSubscribed() { @Test public void assertErrorMultiple() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); TestException e = new TestException(); to.errors().add(e); @@ -947,7 +937,7 @@ public void assertErrorMultiple() { @Test public void errorInPredicate() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onError(new RuntimeException()); try { to.assertError(new Predicate() { @@ -965,9 +955,9 @@ public boolean test(Throwable throwable) throws Exception { @Test public void assertComplete() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.assertComplete(); @@ -992,7 +982,7 @@ public void assertComplete() { @Test public void completeWithoutOnSubscribe() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.onComplete(); @@ -1001,7 +991,7 @@ public void completeWithoutOnSubscribe() { @Test public void completeDelegateThrows() { - TestObserverEx to = new TestObserverEx(new Observer() { + TestObserverEx to = new TestObserverEx<>(new Observer() { @Override public void onSubscribe(Disposable d) { @@ -1025,7 +1015,7 @@ public void onComplete() { }); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.onComplete(); @@ -1037,7 +1027,7 @@ public void onComplete() { @Test public void errorDelegateThrows() { - TestObserverEx to = new TestObserverEx(new Observer() { + TestObserverEx to = new TestObserverEx<>(new Observer() { @Override public void onSubscribe(Disposable d) { @@ -1061,7 +1051,7 @@ public void onComplete() { }); - to.onSubscribe(Disposables.empty()); + to.onSubscribe(Disposable.empty()); try { to.onError(new IOException()); @@ -1073,7 +1063,7 @@ public void onComplete() { @Test public void syncQueueThrows() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.setInitialFusionMode(QueueFuseable.SYNC); Observable.range(1, 5) @@ -1091,19 +1081,19 @@ public void syncQueueThrows() { @Test public void asyncQueueThrows() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.setInitialFusionMode(QueueFuseable.ANY); - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - up + us .map(new Function() { @Override public Object apply(Integer v) throws Exception { throw new TestException(); } }) .subscribe(to); - up.onNext(1); + us.onNext(1); to.assertSubscribed() .assertFuseable() @@ -1129,16 +1119,16 @@ public void errorMeansDisposed() { @Test public void asyncFusion() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); to.setInitialFusionMode(QueueFuseable.ANY); - UnicastSubject up = UnicastSubject.create(); + UnicastSubject us = UnicastSubject.create(); - up + us .subscribe(to); - up.onNext(1); - up.onComplete(); + us.onNext(1); + us.onComplete(); to.assertSubscribed() .assertFuseable() @@ -1148,22 +1138,22 @@ public void asyncFusion() { @Test public void assertValuePredicateEmpty() { - TestObserverEx to = new TestObserverEx(); + assertThrows("No values", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.empty().subscribe(to); + Observable.empty().subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - to.assertValue(new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + to.assertValue(new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValuePredicateMatch() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1).subscribe(to); @@ -1176,52 +1166,52 @@ public void assertValuePredicateMatch() { @Test public void assertValuePredicateNoMatch() { - TestObserverEx to = new TestObserverEx(); + assertThrows("Value not present", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just(1).subscribe(to); + Observable.just(1).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - to.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 1; - } + to.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 1; + } + }); }); } @Test public void assertValuePredicateMatchButMore() { - TestObserverEx to = new TestObserverEx(); + assertThrows("Value present but other values as well", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just(1, 2).subscribe(to); + Observable.just(1, 2).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value present but other values as well"); - to.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + to.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertValueAtPredicateEmpty() { - TestObserverEx to = new TestObserverEx(); + assertThrows("No values", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.empty().subscribe(to); + Observable.empty().subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - to.assertValueAt(0, new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + to.assertValueAt(0, new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValueAtPredicateMatch() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just(1, 2).subscribe(to); @@ -1234,48 +1224,48 @@ public void assertValueAtPredicateMatch() { @Test public void assertValueAtPredicateNoMatch() { - TestObserverEx to = new TestObserverEx(); + assertThrows("Value not present", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just(1, 2, 3).subscribe(to); + Observable.just(1, 2, 3).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - to.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 3; - } + to.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 3; + } + }); }); } @Test public void assertValueAtInvalidIndex() { - TestObserverEx to = new TestObserverEx(); + assertThrows("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just(1, 2).subscribe(to); + Observable.just(1, 2).subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)"); - to.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + to.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertValueAtIndexEmpty() { - TestObserverEx to = new TestObserverEx(); + assertThrows("No values", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.empty().subscribe(to); + Observable.empty().subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - to.assertValueAt(0, "a"); + to.assertValueAt(0, "a"); + }); } @Test public void assertValueAtIndexMatch() { - TestObserverEx to = new TestObserverEx(); + TestObserverEx to = new TestObserverEx<>(); Observable.just("a", "b").subscribe(to); @@ -1284,24 +1274,24 @@ public void assertValueAtIndexMatch() { @Test public void assertValueAtIndexNoMatch() { - TestObserverEx to = new TestObserverEx(); + assertThrows("\nexpected: b (class: String)\ngot: c (class: String) (latch = 0, values = 3, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just("a", "b", "c").subscribe(to); + Observable.just("a", "b", "c").subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("expected: b (class: String) but was: c (class: String) (latch = 0, values = 3, errors = 0, completions = 1)"); - to.assertValueAt(2, "b"); + to.assertValueAt(2, "b"); + }); } @Test public void assertValueAtIndexInvalidIndex() { - TestObserverEx to = new TestObserverEx(); + assertThrows("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestObserverEx to = new TestObserverEx<>(); - Observable.just("a", "b").subscribe(to); + Observable.just("a", "b").subscribe(to); - thrown.expect(AssertionError.class); - thrown.expectMessage("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)"); - to.assertValueAt(2, "c"); + to.assertValueAt(2, "c"); + }); } @Test @@ -1322,8 +1312,8 @@ public void withTag() { @Test public void assertValuesOnly() { - TestObserverEx to = new TestObserverEx(); - to.onSubscribe(Disposables.empty()); + TestObserverEx to = new TestObserverEx<>(); + to.onSubscribe(Disposable.empty()); to.assertValuesOnly(); to.onNext(5); @@ -1335,8 +1325,8 @@ public void assertValuesOnly() { @Test public void assertValuesOnlyThrowsOnUnexpectedValue() { - TestObserverEx to = new TestObserverEx(); - to.onSubscribe(Disposables.empty()); + TestObserverEx to = new TestObserverEx<>(); + to.onSubscribe(Disposable.empty()); to.assertValuesOnly(); to.onNext(5); @@ -1354,8 +1344,8 @@ public void assertValuesOnlyThrowsOnUnexpectedValue() { @Test public void assertValuesOnlyThrowsWhenCompleted() { - TestObserverEx to = new TestObserverEx(); - to.onSubscribe(Disposables.empty()); + TestObserverEx to = new TestObserverEx<>(); + to.onSubscribe(Disposable.empty()); to.onComplete(); @@ -1369,8 +1359,8 @@ public void assertValuesOnlyThrowsWhenCompleted() { @Test public void assertValuesOnlyThrowsWhenErrored() { - TestObserverEx to = new TestObserverEx(); - to.onSubscribe(Disposables.empty()); + TestObserverEx to = new TestObserverEx<>(); + to.onSubscribe(Disposable.empty()); to.onError(new TestException()); diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberEx.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberEx.java index bdc1a6a1da..c3d9837c2b 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberEx.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberEx.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -10,6 +10,7 @@ * 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 io.reactivex.rxjava3.testsupport; import java.util.concurrent.atomic.*; @@ -17,8 +18,9 @@ import org.reactivestreams.*; import io.reactivex.rxjava3.core.FlowableSubscriber; -import io.reactivex.rxjava3.internal.fuseable.*; import io.reactivex.rxjava3.internal.subscriptions.SubscriptionHelper; +import io.reactivex.rxjava3.operators.QueueFuseable; +import io.reactivex.rxjava3.operators.QueueSubscription; /** * An extended test subscriber that records events and allows making assertions about them. @@ -51,7 +53,7 @@ public class TestSubscriberEx private QueueSubscription qs; /** - * Constructs a non-forwarding TestSubscriber with an initial request value of Long.MAX_VALUE. + * Constructs a non-forwarding TestSubscriber with an initial request value of {@link Long#MAX_VALUE}. */ public TestSubscriberEx() { this(EmptySubscriber.INSTANCE, Long.MAX_VALUE); @@ -88,7 +90,7 @@ public TestSubscriberEx(Subscriber actual, long initialRequest) { throw new IllegalArgumentException("Negative initial request not allowed"); } this.downstream = actual; - this.upstream = new AtomicReference(); + this.upstream = new AtomicReference<>(); this.missedRequested = new AtomicLong(initialRequest); } @@ -315,8 +317,8 @@ public final TestSubscriberEx assertFusionMode(int mode) { int m = establishedFusionMode; if (m != mode) { if (qs != null) { - throw new AssertionError("Fusion mode different. Expected: " + fusionModeToString(mode) - + ", actual: " + fusionModeToString(m)); + throw new AssertionError("\nexpected: " + fusionModeToString(mode) + + "\ngot: " + fusionModeToString(m) + "; Fusion mode different"); } else { throw fail("Upstream is not fuseable"); } diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberExTest.java b/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberExTest.java index 24a9e84c9b..f3d19f9cc7 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberExTest.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TestSubscriberExTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -21,8 +21,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.*; -import org.junit.rules.ExpectedException; +import org.junit.Test; import org.mockito.InOrder; import org.reactivestreams.*; @@ -31,20 +30,17 @@ import io.reactivex.rxjava3.exceptions.*; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; -import io.reactivex.rxjava3.internal.fuseable.QueueFuseable; import io.reactivex.rxjava3.internal.subscriptions.*; +import io.reactivex.rxjava3.operators.QueueFuseable; import io.reactivex.rxjava3.processors.*; import io.reactivex.rxjava3.schedulers.Schedulers; public class TestSubscriberExTest extends RxJavaTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void assertTestSubscriberEx() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); oi.subscribe(ts); ts.assertValues(1, 2); @@ -54,34 +50,34 @@ public void assertTestSubscriberEx() { @Test public void assertNotMatchCount() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriberEx ts = new TestSubscriberEx(); - oi.subscribe(ts); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriberEx ts = new TestSubscriberEx<>(); + oi.subscribe(ts); - thrown.expect(AssertionError.class); - - ts.assertValues(1); - ts.assertValueCount(2); - ts.assertTerminated(); + ts.assertValues(1); + ts.assertValueCount(2); + ts.assertTerminated(); + }); } @Test public void assertNotMatchValue() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriberEx ts = new TestSubscriberEx(); - oi.subscribe(ts); - - thrown.expect(AssertionError.class); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriberEx ts = new TestSubscriberEx<>(); + oi.subscribe(ts); - ts.assertValues(1, 3); - ts.assertValueCount(2); - ts.assertTerminated(); + ts.assertValues(1, 3); + ts.assertValueCount(2); + ts.assertTerminated(); + }); } @Test public void assertNeverAtNotMatchingValue() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); oi.subscribe(ts); ts.assertNever(3); @@ -91,40 +87,40 @@ public void assertNeverAtNotMatchingValue() { @Test public void assertNeverAtMatchingValue() { - Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); - TestSubscriberEx ts = new TestSubscriberEx(); - oi.subscribe(ts); + assertThrows(AssertionError.class, () -> { + Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); + TestSubscriberEx ts = new TestSubscriberEx<>(); + oi.subscribe(ts); - ts.assertValues(1, 2); + ts.assertValues(1, 2); - thrown.expect(AssertionError.class); - - ts.assertNever(2); - ts.assertValueCount(2); - ts.assertTerminated(); + ts.assertNever(2); + ts.assertValueCount(2); + ts.assertTerminated(); + }); } @Test public void assertNeverAtMatchingPredicate() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows(AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.just(1, 2).subscribe(ts); - - ts.assertValues(1, 2); + Flowable.just(1, 2).subscribe(ts); - thrown.expect(AssertionError.class); + ts.assertValues(1, 2); - ts.assertNever(new Predicate() { - @Override - public boolean test(final Integer o) throws Exception { - return o == 1; - } + ts.assertNever(new Predicate() { + @Override + public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertNeverAtNotMatchingPredicate() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(2, 3).subscribe(ts); @@ -138,18 +134,18 @@ public boolean test(final Integer o) throws Exception { @Test public void assertTerminalEventNotReceived() { - PublishProcessor p = PublishProcessor.create(); - TestSubscriberEx ts = new TestSubscriberEx(); - p.subscribe(ts); - - p.onNext(1); - p.onNext(2); + assertThrows(AssertionError.class, () -> { + PublishProcessor p = PublishProcessor.create(); + TestSubscriberEx ts = new TestSubscriberEx<>(); + p.subscribe(ts); - thrown.expect(AssertionError.class); + p.onNext(1); + p.onNext(2); - ts.assertValues(1, 2); - ts.assertValueCount(2); - ts.assertTerminated(); + ts.assertValues(1, 2); + ts.assertValueCount(2); + ts.assertTerminated(); + }); } @Test @@ -157,7 +153,7 @@ public void wrappingMock() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2)); Subscriber mockSubscriber = TestHelper.mockSubscriber(); - oi.subscribe(new TestSubscriberEx(mockSubscriber)); + oi.subscribe(new TestSubscriberEx<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -170,7 +166,7 @@ public void wrappingMock() { public void wrappingMockWhenUnsubscribeInvolved() { Flowable oi = Flowable.fromIterable(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)).take(2); Subscriber mockSubscriber = TestHelper.mockSubscriber(); - oi.subscribe(new TestSubscriberEx(mockSubscriber)); + oi.subscribe(new TestSubscriberEx<>(mockSubscriber)); InOrder inOrder = inOrder(mockSubscriber); inOrder.verify(mockSubscriber, times(1)).onNext(1); @@ -182,14 +178,14 @@ public void wrappingMockWhenUnsubscribeInvolved() { @Test public void assertError() { RuntimeException e = new RuntimeException("Oops"); - TestSubscriberEx subscriber = new TestSubscriberEx(); + TestSubscriberEx subscriber = new TestSubscriberEx<>(); Flowable.error(e).subscribe(subscriber); subscriber.assertError(e); } @Test public void awaitTerminalEventWithDurationAndUnsubscribeOnTimeout() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final AtomicBoolean unsub = new AtomicBoolean(false); Flowable.just(1) // @@ -208,28 +204,28 @@ public void run() { @Test(expected = NullPointerException.class) public void nullDelegate1() { - TestSubscriberEx ts = new TestSubscriberEx(null); + TestSubscriberEx ts = new TestSubscriberEx<>(null); ts.onComplete(); } @Test(expected = NullPointerException.class) public void nullDelegate2() { - TestSubscriberEx ts = new TestSubscriberEx(null); + TestSubscriberEx ts = new TestSubscriberEx<>(null); ts.onComplete(); } @Test(expected = NullPointerException.class) public void nullDelegate3() { - TestSubscriberEx ts = new TestSubscriberEx(null, 0L); + TestSubscriberEx ts = new TestSubscriberEx<>(null, 0L); ts.onComplete(); } @Test public void delegate1() { - TestSubscriberEx ts0 = new TestSubscriberEx(); + TestSubscriberEx ts0 = new TestSubscriberEx<>(); ts0.onSubscribe(EmptySubscription.INSTANCE); - TestSubscriberEx ts = new TestSubscriberEx(ts0); + TestSubscriberEx ts = new TestSubscriberEx<>(ts0); ts.onComplete(); ts0.assertTerminated(); @@ -237,8 +233,8 @@ public void delegate1() { @Test public void delegate2() { - TestSubscriberEx ts1 = new TestSubscriberEx(); - TestSubscriberEx ts2 = new TestSubscriberEx(ts1); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(ts1); ts2.onComplete(); ts1.assertComplete(); @@ -246,21 +242,21 @@ public void delegate2() { @Test public void delegate3() { - TestSubscriberEx ts1 = new TestSubscriberEx(); - TestSubscriberEx ts2 = new TestSubscriberEx(ts1, 0L); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); + TestSubscriberEx ts2 = new TestSubscriberEx<>(ts1, 0L); ts2.onComplete(); ts1.assertComplete(); } @Test public void unsubscribed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); assertFalse(ts.isCancelled()); } @Test public void noErrors() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new TestException()); try { ts.assertNoErrors(); @@ -273,7 +269,7 @@ public void noErrors() { @Test public void notCompleted() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); try { ts.assertComplete(); } catch (AssertionError ex) { @@ -285,7 +281,7 @@ public void notCompleted() { @Test public void multipleCompletions() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onComplete(); ts.onComplete(); try { @@ -299,7 +295,7 @@ public void multipleCompletions() { @Test public void completed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onComplete(); try { ts.assertNotComplete(); @@ -312,7 +308,7 @@ public void completed() { @Test public void multipleCompletions2() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onComplete(); ts.onComplete(); try { @@ -326,7 +322,7 @@ public void multipleCompletions2() { @Test public void multipleErrors() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -350,7 +346,7 @@ public void multipleErrors() { @Test public void multipleErrors2() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -371,7 +367,7 @@ public void multipleErrors2() { @Test public void multipleErrors3() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -392,7 +388,7 @@ public void multipleErrors3() { @Test public void multipleErrors4() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); ts.onError(new TestException()); @@ -413,7 +409,7 @@ public void multipleErrors4() { @Test public void differentError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new TestException()); try { ts.assertError(new TestException()); @@ -426,7 +422,7 @@ public void differentError() { @Test public void differentError2() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new RuntimeException()); try { ts.assertError(new TestException()); @@ -439,7 +435,7 @@ public void differentError2() { @Test public void differentError3() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new RuntimeException()); try { ts.assertError(TestException.class); @@ -453,7 +449,7 @@ public void differentError3() { @Test public void differentError4() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new RuntimeException()); try { ts.assertError(Functions.alwaysFalse()); @@ -466,7 +462,7 @@ public void differentError4() { @Test public void errorInPredicate() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new RuntimeException()); try { ts.assertError(new Predicate() { @@ -484,7 +480,7 @@ public boolean test(Throwable throwable) throws Exception { @Test public void noError() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); try { ts.assertError(TestException.class); } catch (AssertionError ex) { @@ -496,7 +492,7 @@ public void noError() { @Test public void noError2() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); try { ts.assertError(new TestException()); } catch (AssertionError ex) { @@ -508,7 +504,7 @@ public void noError2() { @Test public void noError3() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); try { ts.assertError(Functions.alwaysTrue()); } catch (AssertionError ex) { @@ -520,7 +516,7 @@ public void noError3() { @Test public void interruptTerminalEventAwait() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); @@ -546,7 +542,7 @@ public void run() { @Test public void interruptTerminalEventAwaitTimed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); @@ -573,7 +569,7 @@ public void run() { @Test public void interruptTerminalEventAwaitAndUnsubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); @@ -602,7 +598,7 @@ public void run() { @Test public void noTerminalEventBut1Completed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onComplete(); @@ -616,7 +612,7 @@ public void noTerminalEventBut1Completed() { @Test public void noTerminalEventBut1Error() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onError(new TestException()); @@ -630,7 +626,7 @@ public void noTerminalEventBut1Error() { @Test public void noTerminalEventBut1Error1Completed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onComplete(); ts.onError(new TestException()); @@ -645,7 +641,7 @@ public void noTerminalEventBut1Error1Completed() { @Test public void noTerminalEventBut2Errors() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(EmptySubscription.INSTANCE); ts.onError(new TestException()); @@ -667,7 +663,7 @@ public void noTerminalEventBut2Errors() { @Test public void noValues() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onNext(1); try { @@ -680,7 +676,7 @@ public void noValues() { @Test public void valueCount() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onNext(1); ts.onNext(2); @@ -700,7 +696,7 @@ public void onComplete() { throw new TestException(); } }; - TestSubscriberEx ts = new TestSubscriberEx(ts0); + TestSubscriberEx ts = new TestSubscriberEx<>(ts0); try { ts.onComplete(); @@ -719,7 +715,7 @@ public void onError(Throwable e) { throw new TestException(); } }; - TestSubscriberEx ts = new TestSubscriberEx(ts0); + TestSubscriberEx ts = new TestSubscriberEx<>(ts0); try { ts.onError(new RuntimeException()); @@ -732,9 +728,9 @@ public void onError(Throwable e) { @Test public void createDelegate() { - TestSubscriberEx ts1 = new TestSubscriberEx(); + TestSubscriberEx ts1 = new TestSubscriberEx<>(); - TestSubscriberEx ts = new TestSubscriberEx(ts1); + TestSubscriberEx ts = new TestSubscriberEx<>(ts1); ts.assertNotSubscribed(); @@ -791,7 +787,7 @@ public void createDelegate() { @Test public void assertError2() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); try { ts.assertError(TestException.class); @@ -916,7 +912,7 @@ public void valueAndClass() { @Test public void assertFailure() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -939,7 +935,7 @@ public void assertFailure() { @Test public void assertFuseable() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -958,10 +954,10 @@ public void assertFuseable() { } catch (AssertionError ex) { // expected } - ts = new TestSubscriberEx(); + ts = new TestSubscriberEx<>(); ts.setInitialFusionMode(QueueFuseable.ANY); - ts.onSubscribe(new ScalarSubscription(ts, 1)); + ts.onSubscribe(new ScalarSubscription<>(ts, 1)); ts.assertFuseable(); @@ -985,7 +981,7 @@ public void assertFuseable() { @Test public void assertTerminated() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.assertNotTerminated(); @@ -1001,7 +997,7 @@ public void assertTerminated() { @Test public void assertResult() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1038,7 +1034,7 @@ public void assertResult() { @Test public void await() throws Exception { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1064,7 +1060,7 @@ public void await() throws Exception { assertTrue(ts.await(5, TimeUnit.SECONDS)); - final TestSubscriberEx ts1 = new TestSubscriberEx(); + final TestSubscriberEx ts1 = new TestSubscriberEx<>(); ts1.onSubscribe(new BooleanSubscription()); @@ -1080,7 +1076,7 @@ public void run() { @Test public void errors() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1095,7 +1091,7 @@ public void errors() { @Test public void onNext() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1127,7 +1123,7 @@ public void fusionModeToString() { @Test public void multipleTerminals() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1170,7 +1166,7 @@ public void multipleTerminals() { @Test public void assertValue() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1204,13 +1200,13 @@ public void assertValue() { @Test public void onNextMisbehave() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onNext(1); ts.assertError(IllegalStateException.class); - ts = new TestSubscriberEx(); + ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1221,7 +1217,7 @@ public void onNextMisbehave() { @Test public void awaitTerminalEventInterrupt() { - final TestSubscriberEx ts = new TestSubscriberEx(); + final TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1250,7 +1246,7 @@ public void awaitTerminalEventInterrupt() { @Test public void assertTerminated2() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1276,7 +1272,7 @@ public void assertTerminated2() { // expected } - ts = new TestSubscriberEx(); + ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1293,13 +1289,13 @@ public void assertTerminated2() { @Test public void onSubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(null); ts.assertError(NullPointerException.class); - ts = new TestSubscriberEx(); + ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1311,7 +1307,7 @@ public void onSubscribe() { ts.assertError(IllegalStateException.class); - ts = new TestSubscriberEx(); + ts = new TestSubscriberEx<>(); ts.dispose(); bs1 = new BooleanSubscription(); @@ -1324,7 +1320,7 @@ public void onSubscribe() { @Test public void assertValueSequence() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1357,7 +1353,7 @@ public void assertValueSequence() { @Test public void assertEmpty() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); try { ts.assertEmpty(); @@ -1382,7 +1378,7 @@ public void assertEmpty() { @Test public void awaitDoneTimed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Thread.currentThread().interrupt(); @@ -1395,7 +1391,7 @@ public void awaitDoneTimed() { @Test public void assertNotSubscribed() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.assertNotSubscribed(); @@ -1411,7 +1407,7 @@ public void assertNotSubscribed() { @Test public void assertErrorMultiple() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); TestException e = new TestException(); ts.errors().add(e); @@ -1439,7 +1435,7 @@ public void assertErrorMultiple() { @Test public void assertComplete() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); @@ -1466,7 +1462,7 @@ public void assertComplete() { @Test public void completeWithoutOnSubscribe() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onComplete(); @@ -1475,7 +1471,7 @@ public void completeWithoutOnSubscribe() { @Test public void completeDelegateThrows() { - TestSubscriberEx ts = new TestSubscriberEx(new FlowableSubscriber() { + TestSubscriberEx ts = new TestSubscriberEx<>(new FlowableSubscriber() { @Override public void onSubscribe(Subscription s) { @@ -1511,7 +1507,7 @@ public void onComplete() { @Test public void errorDelegateThrows() { - TestSubscriberEx ts = new TestSubscriberEx(new FlowableSubscriber() { + TestSubscriberEx ts = new TestSubscriberEx<>(new FlowableSubscriber() { @Override public void onSubscribe(Subscription s) { @@ -1547,7 +1543,7 @@ public void onComplete() { @Test public void syncQueueThrows() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.setInitialFusionMode(QueueFuseable.SYNC); Flowable.range(1, 5) @@ -1565,7 +1561,7 @@ public void syncQueueThrows() { @Test public void asyncQueueThrows() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.setInitialFusionMode(QueueFuseable.ANY); UnicastProcessor up = UnicastProcessor.create(); @@ -1587,22 +1583,22 @@ public void asyncQueueThrows() { @Test public void assertValuePredicateEmpty() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows("No values", AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.empty().subscribe(ts); + Flowable.empty().subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - ts.assertValue(new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + ts.assertValue(new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValuePredicateMatch() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(1).subscribe(ts); @@ -1615,52 +1611,52 @@ public void assertValuePredicateMatch() { @Test public void assertValuePredicateNoMatch() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows("Value not present", AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.just(1).subscribe(ts); + Flowable.just(1).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - ts.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 1; - } + ts.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 1; + } + }); }); } @Test public void assertValuePredicateMatchButMore() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows("Value present but other values as well", AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.just(1, 2).subscribe(ts); + Flowable.just(1, 2).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value present but other values as well"); - ts.assertValue(new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + ts.assertValue(new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @Test public void assertValueAtPredicateEmpty() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows("No values", AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.empty().subscribe(ts); + Flowable.empty().subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("No values"); - ts.assertValueAt(0, new Predicate() { - @Override public boolean test(final Object o) throws Exception { - return false; - } + ts.assertValueAt(0, new Predicate() { + @Override public boolean test(final Object o) throws Exception { + return false; + } + }); }); } @Test public void assertValueAtPredicateMatch() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); Flowable.just(1, 2).subscribe(ts); @@ -1673,31 +1669,31 @@ public void assertValueAtPredicateMatch() { @Test public void assertValueAtPredicateNoMatch() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows("Value not present", AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.just(1, 2, 3).subscribe(ts); + Flowable.just(1, 2, 3).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Value not present"); - ts.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o != 3; - } + ts.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o != 3; + } + }); }); } @Test public void assertValueAtInvalidIndex() { - TestSubscriberEx ts = new TestSubscriberEx(); + assertThrows("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)", AssertionError.class, () -> { + TestSubscriberEx ts = new TestSubscriberEx<>(); - Flowable.just(1, 2).subscribe(ts); + Flowable.just(1, 2).subscribe(ts); - thrown.expect(AssertionError.class); - thrown.expectMessage("Invalid index: 2 (latch = 0, values = 2, errors = 0, completions = 1)"); - ts.assertValueAt(2, new Predicate() { - @Override public boolean test(final Integer o) throws Exception { - return o == 1; - } + ts.assertValueAt(2, new Predicate() { + @Override public boolean test(final Integer o) throws Exception { + return o == 1; + } + }); }); } @@ -1775,7 +1771,7 @@ public void timeoutIndicated3() throws InterruptedException { @Test public void disposeIndicated() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.cancel(); try { @@ -1824,7 +1820,7 @@ public boolean test(Integer t) throws Exception { @Test public void assertValuesOnly() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); ts.assertValuesOnly(); @@ -1837,7 +1833,7 @@ public void assertValuesOnly() { @Test public void assertValuesOnlyThrowsOnUnexpectedValue() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); ts.assertValuesOnly(); @@ -1856,7 +1852,7 @@ public void assertValuesOnlyThrowsOnUnexpectedValue() { @Test public void assertValuesOnlyThrowsWhenCompleted() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); ts.onComplete(); @@ -1871,7 +1867,7 @@ public void assertValuesOnlyThrowsWhenCompleted() { @Test public void assertValuesOnlyThrowsWhenErrored() { - TestSubscriberEx ts = new TestSubscriberEx(); + TestSubscriberEx ts = new TestSubscriberEx<>(); ts.onSubscribe(new BooleanSubscription()); ts.onError(new TestException()); diff --git a/src/test/java/io/reactivex/rxjava3/testsupport/TimesteppingScheduler.java b/src/test/java/io/reactivex/rxjava3/testsupport/TimesteppingScheduler.java index cb9a81eca0..06804fa282 100644 --- a/src/test/java/io/reactivex/rxjava3/testsupport/TimesteppingScheduler.java +++ b/src/test/java/io/reactivex/rxjava3/testsupport/TimesteppingScheduler.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public boolean isDisposed() { @Override public Disposable schedule(Runnable run, long delay, TimeUnit unit) { run.run(); - return Disposables.disposed(); + return Disposable.disposed(); } @Override diff --git a/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java b/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java index 36457ffd86..582df0fa71 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java +++ b/src/test/java/io/reactivex/rxjava3/validators/BaseTypeAnnotations.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -22,6 +22,12 @@ import io.reactivex.rxjava3.annotations.*; import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.disposables.DisposableContainer; +import io.reactivex.rxjava3.flowables.ConnectableFlowable; +import io.reactivex.rxjava3.observables.ConnectableObservable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.subjects.*; /** * Verifies several properties. @@ -38,16 +44,15 @@ static void checkCheckReturnValueSupport(Class clazz) { StringBuilder b = new StringBuilder(); for (Method m : clazz.getMethods()) { - if (m.getName().equals("bufferSize")) { - continue; - } if (m.getDeclaringClass() == clazz) { - boolean isSubscribeMethod = "subscribe".equals(m.getName()) && m.getParameterTypes().length == 0; + boolean isSubscribeMethod = "subscribe".equals(m.getName()) && + (m.getParameterTypes().length == 0 || m.getParameterTypes()[m.getParameterCount() - 1] == DisposableContainer.class); + boolean isConnectMethod = "connect".equals(m.getName()) && m.getParameterTypes().length == 0; boolean isAnnotationPresent = m.isAnnotationPresent(CheckReturnValue.class); - if (isSubscribeMethod) { + if (isSubscribeMethod || isConnectMethod) { if (isAnnotationPresent) { - b.append("subscribe() method has @CheckReturnValue: ").append(m).append("\r\n"); + b.append(m.getName()).append(" method has @CheckReturnValue: ").append(m).append("\r\n"); } continue; } @@ -83,7 +88,8 @@ static void checkSchedulerSupport(Class clazz) { StringBuilder b = new StringBuilder(); for (Method m : clazz.getMethods()) { - if (m.getName().equals("bufferSize")) { + if (m.getName().equals("bufferSize") + || m.getName().equals("parallelism")) { continue; } if (m.getDeclaringClass() == clazz) { @@ -130,18 +136,24 @@ static void checkBackpressureSupport(Class clazz) { StringBuilder b = new StringBuilder(); for (Method m : clazz.getMethods()) { - if (m.getName().equals("bufferSize")) { + if (m.getName().equals("bufferSize") + || m.getName().equals("parallelism")) { continue; } if (m.getDeclaringClass() == clazz) { - if (clazz == Flowable.class) { + if (clazz == Flowable.class || clazz == ParallelFlowable.class) { if (!m.isAnnotationPresent(BackpressureSupport.class)) { - b.append("No @BackpressureSupport annotation (being Flowable): ").append(m).append("\r\n"); + b.append("No @BackpressureSupport annotation (being ") + .append(clazz.getSimpleName()) + .append("): ").append(m).append("\r\n"); } } else { - if (m.getReturnType() == Flowable.class) { + if (m.getReturnType() == Flowable.class + || m.getReturnType() == ParallelFlowable.class) { if (!m.isAnnotationPresent(BackpressureSupport.class)) { - b.append("No @BackpressureSupport annotation (having Flowable return): ").append(m).append("\r\n"); + b.append("No @BackpressureSupport annotation (having ") + .append(m.getReturnType().getSimpleName()) + .append(" return): ").append(m).append("\r\n"); } } else { boolean found = false; @@ -200,6 +212,86 @@ public void checkReturnValueMaybe() { checkCheckReturnValueSupport(Maybe.class); } + @Test + public void checkReturnValueConnectableObservable() { + checkCheckReturnValueSupport(ConnectableObservable.class); + } + + @Test + public void checkReturnValueConnectableFlowable() { + checkCheckReturnValueSupport(ConnectableFlowable.class); + } + + @Test + public void checkReturnValueParallelFlowable() { + checkCheckReturnValueSupport(ParallelFlowable.class); + } + + @Test + public void checkReturnValueAsyncSubject() { + checkCheckReturnValueSupport(AsyncSubject.class); + } + + @Test + public void checkReturnValueBehaviorSubject() { + checkCheckReturnValueSupport(BehaviorSubject.class); + } + + @Test + public void checkReturnValuePublishSubject() { + checkCheckReturnValueSupport(PublishSubject.class); + } + + @Test + public void checkReturnValueReplaySubject() { + checkCheckReturnValueSupport(ReplaySubject.class); + } + + @Test + public void checkReturnValueUnicastSubject() { + checkCheckReturnValueSupport(UnicastSubject.class); + } + + @Test + public void checkReturnValueAsyncProcessor() { + checkCheckReturnValueSupport(AsyncProcessor.class); + } + + @Test + public void checkReturnValueBehaviorProcessor() { + checkCheckReturnValueSupport(BehaviorProcessor.class); + } + + @Test + public void checkReturnValuePublishProcessor() { + checkCheckReturnValueSupport(PublishProcessor.class); + } + + @Test + public void checkReturnValueReplayProcessor() { + checkCheckReturnValueSupport(ReplayProcessor.class); + } + + @Test + public void checkReturnValueUnicastProcessor() { + checkCheckReturnValueSupport(UnicastProcessor.class); + } + + @Test + public void checkReturnValueMulticastProcessor() { + checkCheckReturnValueSupport(MulticastProcessor.class); + } + + @Test + public void checkReturnValueSubject() { + checkCheckReturnValueSupport(Subject.class); + } + + @Test + public void checkReturnValueFlowableProcessor() { + checkCheckReturnValueSupport(FlowableProcessor.class); + } + @Test public void schedulerSupportFlowable() { checkSchedulerSupport(Flowable.class); @@ -225,6 +317,21 @@ public void schedulerSupportMaybe() { checkSchedulerSupport(Maybe.class); } + @Test + public void schedulerSupportConnectableObservable() { + checkSchedulerSupport(ConnectableObservable.class); + } + + @Test + public void schedulerSupportConnectableFlowable() { + checkSchedulerSupport(ConnectableFlowable.class); + } + + @Test + public void schedulerSupportParallelFlowable() { + checkSchedulerSupport(ParallelFlowable.class); + } + @Test public void backpressureSupportFlowable() { checkBackpressureSupport(Flowable.class); @@ -249,4 +356,19 @@ public void backpressureSupportCompletable() { public void backpressureSupportMaybe() { checkBackpressureSupport(Maybe.class); } + + @Test + public void backpressureSupportConnectableFlowable() { + checkBackpressureSupport(ConnectableFlowable.class); + } + + @Test + public void backpressureSupportConnectableObservable() { + checkBackpressureSupport(ConnectableObservable.class); + } + + @Test + public void backpressureSupportParallelFlowable() { + checkBackpressureSupport(ParallelFlowable.class); + } } diff --git a/src/test/java/io/reactivex/rxjava3/validators/BaseTypeParser.java b/src/test/java/io/reactivex/rxjava3/validators/BaseTypeParser.java index 1dd9f1c1ce..528ee6de97 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/BaseTypeParser.java +++ b/src/test/java/io/reactivex/rxjava3/validators/BaseTypeParser.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -48,7 +48,7 @@ public static class RxMethod { } public static List parse(File f, String baseClassName) throws Exception { - List list = new ArrayList(); + List list = new ArrayList<>(); StringBuilder b = JavadocForAnnotations.readFile(f); diff --git a/src/test/java/io/reactivex/rxjava3/validators/CatchThrowIfFatalCheck.java b/src/test/java/io/reactivex/rxjava3/validators/CatchThrowIfFatalCheck.java new file mode 100644 index 0000000000..45594fe17b --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/CatchThrowIfFatalCheck.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import java.io.File; +import java.nio.file.Files; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Check if a {@code catch(Throwable} is followed by a + * {@code Exceptions.throwIfFatal}, {@code Exceptions.wrapOrThrow} + * or {@code fail} call. + * @since 3.0.0 + */ +public class CatchThrowIfFatalCheck { + + @Test + public void check() throws Exception { + File f = TestHelper.findSource("Flowable"); + if (f == null) { + System.out.println("Unable to find sources of RxJava"); + return; + } + + Queue dirs = new ArrayDeque<>(); + + StringBuilder fail = new StringBuilder(); + int errors = 0; + + File parent = f.getParentFile().getParentFile(); + + dirs.offer(new File(parent.getAbsolutePath().replace('\\', '/'))); + + while (!dirs.isEmpty()) { + f = dirs.poll(); + + File[] list = f.listFiles(); + if (list != null && list.length != 0) { + + for (File u : list) { + if (u.isDirectory()) { + dirs.offer(u); + } else { + List lines = Files.readAllLines(u.toPath()); + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i).trim(); + + if (line.startsWith("} catch (Throwable ")) { + String next = lines.get(i + 1).trim(); + boolean throwIfFatal = next.contains("Exceptions.throwIfFatal"); + boolean wrapOrThrow = next.contains("ExceptionHelper.wrapOrThrow"); + boolean failCall = next.startsWith("fail("); + + if (!(throwIfFatal || wrapOrThrow || failCall)) { + errors++; + fail.append("Missing Exceptions.throwIfFatal\n ") + .append(next) + .append("\n at ") + .append(u.getName().replace(".java", "")) + .append(".method(") + .append(u.getName()) + .append(":") + .append(i + 1) + .append(")\n") + ; + } + } + } + } + } + } + } + + if (errors != 0) { + fail.insert(0, "Found " + errors + " cases\n"); + System.out.println(fail); + throw new AssertionError(fail.toString()); + } + } +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/CheckLocalVariablesInTests.java b/src/test/java/io/reactivex/rxjava3/validators/CheckLocalVariablesInTests.java index 221896fea2..0ec5952f8b 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/CheckLocalVariablesInTests.java +++ b/src/test/java/io/reactivex/rxjava3/validators/CheckLocalVariablesInTests.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -49,7 +49,7 @@ static void findPattern(String pattern, boolean checkMain) throws Exception { return; } - Queue dirs = new ArrayDeque(); + Queue dirs = new ArrayDeque<>(); StringBuilder fail = new StringBuilder(); fail.append("The following code pattern was found: ").append(pattern).append("\n"); @@ -94,7 +94,15 @@ static void findPattern(String pattern, boolean checkMain) throws Exception { .append(fname) .append("#L").append(lineNum) .append(" ").append(line) - .append("\n"); + .append("\n") + .append(" at ") + .append(fname.replace(".java", "")) + .append(".method(") + .append(fname) + .append(":") + .append(lineNum) + .append(")\n"); + total++; } } @@ -111,9 +119,7 @@ static void findPattern(String pattern, boolean checkMain) throws Exception { } } if (total != 0) { - fail.append("Found ") - .append(total) - .append(" instances"); + fail.insert(0, "Found " + total + " instances"); System.out.println(fail); throw new AssertionError(fail.toString()); } @@ -149,6 +155,16 @@ public void publishProcessorAsPs() throws Exception { findPattern("PublishProcessor<.*>\\s+ps"); } + @Test + public void unicastSubjectAsUp() throws Exception { + findPattern("UnicastSubject<.*>\\s+up"); + } + + @Test + public void unicastProcessorAsUs() throws Exception { + findPattern("UnicastProcessor<.*>\\s+us"); + } + @Test public void behaviorProcessorAsBs() throws Exception { findPattern("BehaviorProcessor<.*>\\s+bs"); diff --git a/src/test/java/io/reactivex/rxjava3/validators/FixLicenseHeaders.java b/src/test/java/io/reactivex/rxjava3/validators/FixLicenseHeaders.java index f069ec43cf..5d7223cbd1 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/FixLicenseHeaders.java +++ b/src/test/java/io/reactivex/rxjava3/validators/FixLicenseHeaders.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -26,7 +26,7 @@ public class FixLicenseHeaders { String[] header = { - "/**", + "/*", " * Copyright (c) 2016-present, RxJava Contributors.", " *", " * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in", @@ -52,7 +52,7 @@ public void checkAndUpdateLicenses() throws Exception { return; } - Queue dirs = new ArrayDeque(); + Queue dirs = new ArrayDeque<>(); File parent = f.getParentFile().getParentFile(); dirs.offer(parent); @@ -73,7 +73,7 @@ public void checkAndUpdateLicenses() throws Exception { } else { if (u.getName().endsWith(".java")) { - List lines = new ArrayList(); + List lines = new ArrayList<>(); BufferedReader in = new BufferedReader(new FileReader(u)); try { for (;;) { @@ -88,7 +88,7 @@ public void checkAndUpdateLicenses() throws Exception { in.close(); } - if (!lines.get(0).equals(header[0]) && !lines.get(1).equals(header[1])) { + if (!lines.get(0).equals(header[0]) || !lines.get(1).equals(header[1])) { fail.append("java.lang.RuntimeException: missing header added, refresh and re-run tests!\r\n") .append(" at ") ; diff --git a/src/test/java/io/reactivex/rxjava3/validators/InternalWrongNaming.java b/src/test/java/io/reactivex/rxjava3/validators/InternalWrongNaming.java index 984d6bbbd5..bbf86c4188 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/InternalWrongNaming.java +++ b/src/test/java/io/reactivex/rxjava3/validators/InternalWrongNaming.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -62,7 +62,7 @@ static void checkInternalOperatorNaming(String baseClassName, String consumerCla fail.append("java.lang.RuntimeException: " + g.getName() + " mentions " + consumerClassName) .append("\r\n at io.reactivex.internal.operators.") .append(baseClassName.toLowerCase()).append(".").append(g.getName().replace(".java", "")) - .append(" (").append(g.getName()).append(":").append(i + 1).append(")\r\n\r\n"); + .append(".method(").append(g.getName()).append(":").append(i + 1).append(")\r\n\r\n"); count++; } @@ -81,7 +81,7 @@ static void checkInternalOperatorNaming(String baseClassName, String consumerCla } static List readFile(File u) throws Exception { - List lines = new ArrayList(); + List lines = new ArrayList<>(); BufferedReader in = new BufferedReader(new FileReader(u)); try { @@ -170,6 +170,9 @@ public void flowableNoObserver() throws Exception { "FlowableCountSingle", "FlowableElementAtMaybe", "FlowableElementAtSingle", + "FlowableElementAtMaybePublisher", + "FlowableElementAtSinglePublisher", + "FlowableFromCompletable", "FlowableSingleSingle", "FlowableSingleMaybe", "FlowableLastMaybe", diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java new file mode 100644 index 0000000000..698fdf7e83 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocCodesAndLinks.java @@ -0,0 +1,436 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import java.io.File; +import java.nio.file.Files; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Scan the Javadocs of a source and check if mentions of other classes, + * interfaces and enums are using at-link and at-code wrapping for style. + *

    + * The check ignores html tag content on a line, @see and @throws entries + * and <code></code> lines. + */ +public class JavadocCodesAndLinks { + + @Test + public void checkFlowable() throws Exception { + checkSource("Flowable", "io.reactivex.rxjava3.core"); + } + + @Test + public void checkCompletable() throws Exception { + checkSource("Completable", "io.reactivex.rxjava3.core"); + } + + @Test + public void checkSingle() throws Exception { + checkSource("Single", "io.reactivex.rxjava3.core"); + } + + @Test + public void checkMaybe() throws Exception { + checkSource("Maybe", "io.reactivex.rxjava3.core"); + } + + @Test + public void checkObservable() throws Exception { + checkSource("Observable", "io.reactivex.rxjava3.core"); + } + + @Test + public void checkParallelFlowable() throws Exception { + checkSource("ParallelFlowable", "io.reactivex.rxjava3.parallel"); + } + + @Test + public void checkCompositeDisposable() throws Exception { + checkSource("CompositeDisposable", "io.reactivex.rxjava3.disposables"); + } + + @Test + public void checkConnectableFlowable() throws Exception { + checkSource("ConnectableFlowable", "io.reactivex.rxjava3.flowables"); + } + + @Test + public void checkConnectableObservable() throws Exception { + checkSource("ConnectableObservable", "io.reactivex.rxjava3.observables"); + } + + @Test + public void checkSchedulers() throws Exception { + checkSource("Schedulers", "io.reactivex.rxjava3.schedulers"); + } + + static void checkSource(String baseClassName, String packageName) throws Exception { + File f = TestHelper.findSource(baseClassName, packageName); + if (f == null) { + return; + } + + StringBuilder errors = new StringBuilder(2048); + int errorCount = 0; + + List lines = Files.readAllLines(f.toPath()); + + List docs = new ArrayList<>(); + + // i = 1 skip the header javadoc + for (int i = 1; i < lines.size(); i++) { + + if (lines.get(i).trim().equals("/**")) { + docs.clear(); + + boolean skipCode = false; + for (int j = i + 1; j < lines.size(); j++) { + String line = lines.get(j).trim(); + if (line.contains("")) { + skipCode = true; + } + if (line.equals("*/")) { + break; + } + if (!skipCode) { + // strip + line = stripTags(line); + if (line.startsWith("@see")) { + docs.add(""); + } + else if (line.startsWith("@throws") || line.startsWith("@param")) { + int space = line.indexOf(' '); + if (space < 0) { + docs.add(""); + } else { + space = line.indexOf(" ", space + 1); + if (space < 0) { + docs.add(""); + } else { + docs.add(line.substring(space + 1)); + } + } + } else { + docs.add(line); + } + } else { + docs.add(""); + } + if (line.contains("")) { + skipCode = false; + } + } + + for (String name : NAMES) { + boolean isHostType = name.equals(baseClassName); + boolean isAlwaysCode = ALWAYS_CODE.contains(name); + String asLink = "{@link " + name + "}"; + String asCode = "{@code " + name + "}"; + boolean seenBefore = false; + + for (int j = 0; j < docs.size(); j++) { + String line = docs.get(j); + + int idxLink = line.indexOf(asLink); + if (idxLink >= 0 && !isHostType && !isAlwaysCode) { + int k = idxLink + asLink.length(); + for (;;) { + int jdxLink = line.indexOf(asLink, k); + if (jdxLink < 0) { + break; + } + if (jdxLink >= 0) { + errorCount++; + errors.append("The subsequent mention should be code: ") + .append("{@code ").append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } + k = jdxLink + asLink.length(); + } + } + if (seenBefore) { + if (idxLink >= 0 && !isHostType && !isAlwaysCode) { + errorCount++; + errors.append("The subsequent mention should be code: ") + .append("{@code ").append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } + } else { + int idxCode = line.indexOf(asCode); + + if (isHostType) { + if (idxLink >= 0) { + errorCount++; + errors.append("The host type mention should be code: ") + .append("{@code ").append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } + } else { + if ((idxLink < 0 && idxCode >= 0 && !isAlwaysCode) + || (idxLink >= 0 && idxCode >= 0 && idxCode < idxLink)) { + errorCount++; + if (isAlwaysCode) { + errors.append("The first mention should be code: ") + .append("{@code ") + ; + } else { + errors.append("The first mention should be link: ") + .append("{@link ") + ; + } + errors + .append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } + } + + seenBefore = idxLink >= 0 || idxCode >= 0; + } + + // strip out all existing {@code } and {@link } instances + String noCurly = removeCurlies(line); + + int k = 0; + + for (;;) { + int idx = noCurly.indexOf(name, k); + if (idx < 0) { + break; + } + k = idx + name.length(); + + if (isHostType) { + errorCount++; + errors.append("The host type mention should be code: ") + .append("{@code ").append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } + else if (!seenBefore) { + errorCount++; + if (isAlwaysCode) { + errors.append("The first mention should be code: ") + .append("{@code "); + } else { + errors.append("The first mention should be link: ") + .append("{@link "); + } + errors + .append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } else { + errorCount++; + errors.append("The subsequent mention should be code: ") + .append("{@code ").append(name) + .append("}\r\n at ") + .append(packageName) + .append(".") + .append(baseClassName) + .append(".method(") + .append(baseClassName) + .append(".java:") + .append(i + 2 + j) + .append(")\r\n"); + } + + seenBefore = true; + } + } + } + + i += docs.size(); + + if (errorCount >= ERROR_LIMIT) { + break; + } + } + } + + if (errorCount != 0) { + errors.insert(0, "Found " + (errorCount > ERROR_LIMIT ? ERROR_LIMIT + "+" : errorCount + "") + " cases\r\n"); + throw new AssertionError(errors.toString()); + } + } + + static String removeCurlies(String input) { + StringBuilder result = new StringBuilder(input.length()); + + boolean skip = false; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '{') { + skip = true; + } + if (!skip) { + result.append(c); + } + if (c == '}') { + skip = false; + } + } + + return result.toString(); + } + + static String stripTags(String input) { + StringBuilder result = new StringBuilder(input.length()); + result.append(input, input.length() > 1 ? 2 : 1, input.length()); + + clearTag(result, ""); + clearTag(result, "", ""); + clearTag(result, "", ""); + clearTag(result, "", ""); + clearTag(result, ""); + + return result.toString(); + } + + static void clearTag(StringBuilder builder, String startTag, String endTag) { + int k = 0; + for (;;) { + int j = builder.indexOf(startTag, k); + if (j < 0) { + break; + } + + int e = builder.indexOf(endTag, j); + if (e < 0) { + e = builder.length(); + } + + blankRange(builder, j, e); + + k = e + endTag.length(); + } + } + + static void blankRange(StringBuilder builder, int start, int end) { + for (int i = start; i < end; i++) { + int c = builder.charAt(i); + if (c != '\r' && c != '\n') { + builder.setCharAt(i, ' '); + } + } + } + + static final List NAMES = Arrays.asList( + "Flowable", "Observable", "Maybe", "Single", "Completable", "ParallelFlowable", + + "Publisher", "ObservableSource", "MaybeSource", "SingleSource", "CompletableSource", + + "FlowableSubscriber", "Subscriber", "Observer", "MaybeObserver", "SingleObserver", "CompletableObserver", + + "FlowableOperator", "ObservableOperator", "MaybeOperator", "SingleOperator", "CompletableOperator", + + "FlowableOnSubscribe", "ObservableOnSubscribe", "MaybeOnSubscribe", "SingleOnSubscribe", "CompletableOnSubscribe", + + "FlowableTransformer", "ObservableTransformer", "MaybeTransformer", "SingleTransformer", "CompletableTransformer", "ParallelTransformer", + + "FlowableConverter", "ObservableConverter", "MaybeConverter", "SingleConverter", "CompletableConverter", + + "FlowableEmitter", "ObservableEmitter", "MaybeEmitter", "SingleEmitter", "CompletableEmitter", + + "Iterable", "Stream", + + "Function", "BiFunction", "Function3", "Function4", "Function5", "Function6", "Function7", "Function8", "Function9", + + "Action", "Runnable", "Disposable", "Subscription", "Consumer", "BiConsumer", "Future", + + "Supplier", "Callable", "TimeUnit", + + "BackpressureOverflowStrategy", "ParallelFailureHandling", + + "Exception", "Throwable", "NullPointerException", "IllegalStateException", "IllegalArgumentException", "MissingBackpressureException", "UndeliverableException", + "OutOfMemoryError", "StackOverflowError", "NoSuchElementException", "ClassCastException", "CompositeException", + "RuntimeException", "Error", "TimeoutException", "OnErrorNotImplementedException", + + "false", "true", "onNext", "onError", "onComplete", "onSuccess", "onSubscribe", "null", + + "ConnectableFlowable", "ConnectableObservable", "Subject", "FlowableProcessor", "Processor", "Scheduler", + + "Optional", "CompletionStage", "Collector", "Collectors", "Schedulers", "RxJavaPlugins", "CompletableFuture", + + "Object", "Integer", "Long", "Boolean", "LongConsumer", "BooleanSupplier", + + "GroupedFlowable", "GroupedObservable", "UnicastSubject", "UnicastProcessor", + + "Notification", "Comparable", "Comparator", "Collection", + + "SafeSubscriber", "SafeObserver", + + "List", "ArrayList", "HashMap", "HashSet", "CharSequence", + + "TestSubscriber", "TestObserver", "Class", + + "ThreadFactory", "Runnable", "Executor", "ExecutorService", "Executors", "RejectedExecutionException" + ); + + static final Set ALWAYS_CODE = new HashSet<>(Arrays.asList( + "false", "true", "null", "onSuccess", "onNext", "onError", "onComplete", "onSubscribe" + )); + + static final int ERROR_LIMIT = 5000; +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocFindUnescapedAngleBrackets.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocFindUnescapedAngleBrackets.java index 7ebdd1e8a1..016e1b02a2 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocFindUnescapedAngleBrackets.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocFindUnescapedAngleBrackets.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -12,6 +12,7 @@ */ package io.reactivex.rxjava3.validators; + import java.io.*; import java.util.*; @@ -31,7 +32,7 @@ public void find() throws Exception { base = base.getParentFile().getParentFile(); - Queue files = new ArrayDeque(); + Queue files = new ArrayDeque<>(); files.offer(base.listFiles()); diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java index 2f16d05c14..1c683783d6 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocForAnnotations.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -95,7 +95,7 @@ static final void scanFor(StringBuilder sourceCode, String annotation, String in ; int lc = lineNumber(sourceCode, idx); - e.append(" at io.reactivex.").append(baseClassName) + e.append(" at io.reactivex.rxjava3.core.").append(baseClassName) .append(" (").append(baseClassName).append(".java:") .append(lc).append(")").append("\r\n\r\n"); } @@ -125,7 +125,13 @@ static final void scanForBadMethod(StringBuilder sourceCode, String annotation, if (k >= 0 && k <= idx) { int ll = sourceCode.indexOf("You specify", k); + if (ll < 0) { + ll = sourceCode.indexOf("you specify", k); + } int lm = sourceCode.indexOf("This operator", k); + if (lm < 0) { + lm = sourceCode.indexOf("this operator", k); + } if ((ll < 0 || ll > idx) && (lm < 0 || lm > idx)) { int n = sourceCode.indexOf("{@code ", k); @@ -138,27 +144,30 @@ static final void scanForBadMethod(StringBuilder sourceCode, String annotation, if (m < idx) { String mname = sourceCode.substring(n + 7, m); - int q = sourceCode.indexOf("@SuppressWarnings({", idx); + if (!"Scheduler".equals(mname)) { - int o = sourceCode.indexOf("{", idx); + int q = sourceCode.indexOf("@SuppressWarnings({", idx); - if (q + 18 == o) { - o = sourceCode.indexOf("{", q + 20); - } + int o = sourceCode.indexOf("{", idx); + + if (q + 18 == o) { + o = sourceCode.indexOf("{", q + 20); + } - if (o >= 0) { + if (o >= 0) { - int p = sourceCode.indexOf(" " + mname + "(", idx); + int p = sourceCode.indexOf(" " + mname + "(", idx); - if (p < 0 || p > o) { - // when printed on the console, IDEs will create a clickable link to help navigate to the offending point - e.append("java.lang.RuntimeException: wrong method name in description of ").append(inDoc).append(" '").append(mname).append("'\r\n") - ; - int lc = lineNumber(sourceCode, idx); + if (p < 0 || p > o) { + // when printed on the console, IDEs will create a clickable link to help navigate to the offending point + e.append("java.lang.RuntimeException: wrong method name in description of ").append(inDoc).append(" '").append(mname).append("'\r\n") + ; + int lc = lineNumber(sourceCode, idx); - e.append(" at io.reactivex.").append(baseClassName) - .append(" (").append(baseClassName).append(".java:") - .append(lc).append(")").append("\r\n\r\n"); + e.append(" at io.reactivex.rxjava3.core.").append(baseClassName) + .append(".method(").append(baseClassName).append(".java:") + .append(lc).append(")").append("\r\n"); + } } } } diff --git a/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java b/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java index e8219df148..3ab41589a3 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java +++ b/src/test/java/io/reactivex/rxjava3/validators/JavadocWording.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -57,8 +57,8 @@ public void maybeDocRefersToMaybeTypes() throws Exception { && !m.signature.contains("Flowable") && !m.signature.contains("Observable") && !m.signature.contains("ObservableSource")) { - e.append("java.lang.RuntimeException: Maybe doc mentions onNext but no Flowable/Observable in signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions onNext but no Flowable/Observable in signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -74,8 +74,8 @@ public void maybeDocRefersToMaybeTypes() throws Exception { && !m.signature.contains("Flowable") && !m.signature.contains("TestSubscriber") ) { - e.append("java.lang.RuntimeException: Maybe doc mentions Subscriber but not using Flowable\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions Subscriber but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -90,8 +90,8 @@ public void maybeDocRefersToMaybeTypes() throws Exception { if (!m.signature.contains("Publisher") && !m.signature.contains("Flowable") ) { - e.append("java.lang.RuntimeException: Maybe doc mentions Subscription but not using Flowable\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions Subscription but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -108,8 +108,8 @@ public void maybeDocRefersToMaybeTypes() throws Exception { && !m.signature.contains("TestObserver")) { if (idx < 5 || !m.javadoc.substring(idx - 5, idx + 8).equals("MaybeObserver")) { - e.append("java.lang.RuntimeException: Maybe doc mentions Observer but not using Observable\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions Observer but not using Observable\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -124,8 +124,8 @@ public void maybeDocRefersToMaybeTypes() throws Exception { if (idx >= 0) { if (!m.signature.contains("Publisher")) { if (idx == 0 || !m.javadoc.substring(idx - 1, idx + 9).equals("(Publisher")) { - e.append("java.lang.RuntimeException: Maybe doc mentions Publisher but not in the signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions Publisher but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -139,8 +139,11 @@ public void maybeDocRefersToMaybeTypes() throws Exception { int idx = m.javadoc.indexOf("Flowable", jdx); if (idx >= 0) { if (!m.signature.contains("Flowable")) { - e.append("java.lang.RuntimeException: Maybe doc mentions Flowable but not in the signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*Flowable"); + if (!p.matcher(m.javadoc).find()) { + e.append("java.lang.RuntimeException: Maybe doc mentions Flowable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } } jdx = idx + 6; } else { @@ -150,12 +153,12 @@ public void maybeDocRefersToMaybeTypes() throws Exception { jdx = 0; for (;;) { int idx = m.javadoc.indexOf("Single", jdx); - if (idx >= 0) { + if (idx >= 0 && m.javadoc.indexOf("Single#", jdx) != idx) { int j = m.javadoc.indexOf("#toSingle", jdx); int k = m.javadoc.indexOf("{@code Single", jdx); if (!m.signature.contains("Single") && (j + 3 != idx && k + 7 != idx)) { - e.append("java.lang.RuntimeException: Maybe doc mentions Single but not in the signature\r\n at io.reactivex.") - .append("Maybe(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions Single but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -167,8 +170,8 @@ public void maybeDocRefersToMaybeTypes() throws Exception { int idx = m.javadoc.indexOf("SingleSource", jdx); if (idx >= 0) { if (!m.signature.contains("SingleSource")) { - e.append("java.lang.RuntimeException: Maybe doc mentions SingleSource but not in the signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions SingleSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -180,8 +183,11 @@ public void maybeDocRefersToMaybeTypes() throws Exception { int idx = m.javadoc.indexOf("Observable", jdx); if (idx >= 0) { if (!m.signature.contains("Observable")) { - e.append("java.lang.RuntimeException: Maybe doc mentions Observable but not in the signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*Observable"); + if (!p.matcher(m.javadoc).find()) { + e.append("java.lang.RuntimeException: Maybe doc mentions Observable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } } jdx = idx + 6; } else { @@ -193,16 +199,19 @@ public void maybeDocRefersToMaybeTypes() throws Exception { int idx = m.javadoc.indexOf("ObservableSource", jdx); if (idx >= 0) { if (!m.signature.contains("ObservableSource")) { - e.append("java.lang.RuntimeException: Maybe doc mentions ObservableSource but not in the signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Maybe doc mentions ObservableSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { break; } } + + checkAtReturnAndSignatureMatch("Maybe", m, e, "Flowable", "Observable", "Maybe", "Single", "Completable", "Disposable", "Iterable", "Stream", "Future", "CompletionStage"); + aOrAn(e, m, "Maybe"); - missingClosingDD(e, m, "Maybe"); + missingClosingDD(e, m, "Maybe", "io.reactivex.rxjava3.core"); backpressureMentionedWithoutAnnotation(e, m, "Maybe"); } } @@ -233,8 +242,8 @@ public void flowableDocRefersToFlowableTypes() throws Exception { && !m.signature.contains("MaybeSource") && !m.signature.contains("Single") && !m.signature.contains("SingleSource")) { - e.append("java.lang.RuntimeException: Flowable doc mentions onSuccess\r\n at io.reactivex.") - .append("Flowable (Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Flowable doc mentions onSuccess\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -244,12 +253,42 @@ public void flowableDocRefersToFlowableTypes() throws Exception { } jdx = 0; for (;;) { - int idx = m.javadoc.indexOf("Observer", jdx); + int idx = m.javadoc.indexOf(" Observer", jdx); if (idx >= 0) { if (!m.signature.contains("ObservableSource") && !m.signature.contains("Observable")) { - e.append("java.lang.RuntimeException: Flowable doc mentions Observer but not using Flowable\r\n at io.reactivex.") - .append("Flowable (Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Flowable doc mentions Observer but not using Observable\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf(" SingleObserver", jdx); + if (idx >= 0) { + if (!m.signature.contains("SingleSource") + && !m.signature.contains("Single")) { + e.append("java.lang.RuntimeException: Flowable doc mentions SingleObserver but not using Single\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf(" MaybeObserver", jdx); + if (idx >= 0) { + if (!m.signature.contains("MaybeSource") + && !m.signature.contains("Maybe")) { + e.append("java.lang.RuntimeException: Flowable doc mentions MaybeObserver but not using Maybe\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -270,11 +309,12 @@ public void flowableDocRefersToFlowableTypes() throws Exception { && !m.signature.contains("Maybe") && !m.signature.contains("MaybeSource") && !m.signature.contains("Disposable") + && !m.signature.contains("void subscribe") ) { CharSequence subSequence = m.javadoc.subSequence(idx - 6, idx + 11); if (idx < 6 || !subSequence.equals("{@link Disposable")) { - e.append("java.lang.RuntimeException: Flowable doc mentions Disposable but not using Flowable\r\n at io.reactivex.") - .append("Flowable (Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Flowable doc mentions Disposable but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -288,8 +328,8 @@ public void flowableDocRefersToFlowableTypes() throws Exception { int idx = m.javadoc.indexOf("Observable", jdx); if (idx >= 0) { if (!m.signature.contains("Observable")) { - e.append("java.lang.RuntimeException: Flowable doc mentions Observable but not in the signature\r\n at io.reactivex.") - .append("Flowable (Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Flowable doc mentions Observable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -302,16 +342,19 @@ public void flowableDocRefersToFlowableTypes() throws Exception { int idx = m.javadoc.indexOf("ObservableSource", jdx); if (idx >= 0) { if (!m.signature.contains("ObservableSource")) { - e.append("java.lang.RuntimeException: Flowable doc mentions ObservableSource but not in the signature\r\n at io.reactivex.") - .append("Flowable (Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Flowable doc mentions ObservableSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { break; } } + + checkAtReturnAndSignatureMatch("Flowable", m, e, "Flowable", "Observable", "Maybe", "Single", "Completable", "ConnectableFlowable", "ParallelFlowable", "Disposable", "Iterable", "Stream", "Future", "CompletionStage"); + aOrAn(e, m, "Flowable"); - missingClosingDD(e, m, "Flowable"); + missingClosingDD(e, m, "Flowable", "io.reactivex.rxjava3.core"); backpressureMentionedWithoutAnnotation(e, m, "Flowable"); } } @@ -323,6 +366,148 @@ public void flowableDocRefersToFlowableTypes() throws Exception { } } + @Test + public void parallelFlowableDocRefersToCorrectTypes() throws Exception { + List list = BaseTypeParser.parse(TestHelper.findSource("ParallelFlowable", "io.reactivex.rxjava3.parallel"), "ParallelFlowable"); + + assertFalse(list.isEmpty()); + + StringBuilder e = new StringBuilder(); + + for (RxMethod m : list) { + int jdx; + if (m.javadoc != null) { + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf("onSuccess", jdx); + if (idx >= 0) { + if (!m.signature.contains("Maybe") + && !m.signature.contains("MaybeSource") + && !m.signature.contains("Single") + && !m.signature.contains("SingleSource")) { + e.append("java.lang.RuntimeException: Flowable doc mentions onSuccess\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf(" Observer", jdx); + if (idx >= 0) { + if (!m.signature.contains("ObservableSource") + && !m.signature.contains("Observable")) { + e.append("java.lang.RuntimeException: Flowable doc mentions Observer but not using Observable\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf(" SingleObserver", jdx); + if (idx >= 0) { + if (!m.signature.contains("SingleSource") + && !m.signature.contains("Single")) { + e.append("java.lang.RuntimeException: Flowable doc mentions SingleObserver but not using Single\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf(" MaybeObserver", jdx); + if (idx >= 0) { + if (!m.signature.contains("MaybeSource") + && !m.signature.contains("Maybe")) { + e.append("java.lang.RuntimeException: Flowable doc mentions MaybeObserver but not using Maybe\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf(" Disposable", jdx); + if (idx >= 0) { + if (!m.signature.contains("Observable") + && !m.signature.contains("ObservableSource") + && !m.signature.contains("Single") + && !m.signature.contains("SingleSource") + && !m.signature.contains("Completable") + && !m.signature.contains("CompletableSource") + && !m.signature.contains("Maybe") + && !m.signature.contains("MaybeSource") + && !m.signature.contains("Disposable") + ) { + CharSequence subSequence = m.javadoc.subSequence(idx - 6, idx + 11); + if (idx < 6 || !subSequence.equals("{@link Disposable")) { + e.append("java.lang.RuntimeException: Flowable doc mentions Disposable but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf("Observable", jdx); + if (idx >= 0) { + if (!m.signature.contains("Observable")) { + e.append("java.lang.RuntimeException: Flowable doc mentions Observable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + + jdx = idx + 6; + } else { + break; + } + } + jdx = 0; + for (;;) { + int idx = m.javadoc.indexOf("ObservableSource", jdx); + if (idx >= 0) { + if (!m.signature.contains("ObservableSource")) { + e.append("java.lang.RuntimeException: Flowable doc mentions ObservableSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Flowable.method(Flowable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + } + jdx = idx + 6; + } else { + break; + } + } + + checkAtReturnAndSignatureMatch("ParallelFlowable", m, e, "Flowable", "Observable", "Maybe", "Single", "Completable", "ConnectableFlowable", "ParallelFlowable", "Disposable", "Iterable", "Stream", "Future", "CompletionStage"); + + aOrAn(e, m, "ParallelFlowable"); + missingClosingDD(e, m, "ParallelFlowable", "io.reactivex.rxjava3.parallel"); + backpressureMentionedWithoutAnnotation(e, m, "ParallelFlowable"); + } + } + + if (e.length() != 0) { + System.out.println(e); + + fail(e.toString()); + } + } + @Test public void observableDocRefersToObservableTypes() throws Exception { List list = BaseTypeParser.parse(TestHelper.findSource("Observable"), "Observable"); @@ -342,8 +527,8 @@ public void observableDocRefersToObservableTypes() throws Exception { && !m.signature.contains("MaybeSource") && !m.signature.contains("Single") && !m.signature.contains("SingleSource")) { - e.append("java.lang.RuntimeException: Observable doc mentions onSuccess\r\n at io.reactivex.") - .append("Observable (Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Observable doc mentions onSuccess\r\n at io.reactivex.rxjava3.core.") + .append("Observable.method(Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -358,8 +543,8 @@ public void observableDocRefersToObservableTypes() throws Exception { if (!m.signature.contains("Flowable") && !m.signature.contains("Publisher") ) { - e.append("java.lang.RuntimeException: Observable doc mentions Subscription but not using Flowable\r\n at io.reactivex.") - .append("Observable (Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Observable doc mentions Subscription but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Observable.method(Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -373,8 +558,8 @@ public void observableDocRefersToObservableTypes() throws Exception { if (idx >= 0) { if (!m.signature.contains("Flowable")) { if (idx < 6 || !m.javadoc.substring(idx - 6, idx + 8).equals("@link Flowable")) { - e.append("java.lang.RuntimeException: Observable doc mentions Flowable but not in the signature\r\n at io.reactivex.") - .append("Observable (Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Observable doc mentions Flowable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Observable.method(Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -388,8 +573,8 @@ public void observableDocRefersToObservableTypes() throws Exception { int idx = m.javadoc.indexOf("Publisher", jdx); if (idx >= 0) { if (!m.signature.contains("Publisher")) { - e.append("java.lang.RuntimeException: Observable doc mentions Publisher but not in the signature\r\n at io.reactivex.") - .append("Observable (Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Observable doc mentions Publisher but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Observable.method(Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -402,8 +587,8 @@ public void observableDocRefersToObservableTypes() throws Exception { if (idx >= 0) { if (!m.signature.contains("Publisher") && !m.signature.contains("Flowable")) { - e.append("java.lang.RuntimeException: Observable doc mentions Subscriber but not using Flowable\r\n at io.reactivex.") - .append("Observable (Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Observable doc mentions Subscriber but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Observable.method(Observable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -411,8 +596,10 @@ public void observableDocRefersToObservableTypes() throws Exception { break; } } + checkAtReturnAndSignatureMatch("Observable", m, e, "Flowable", "Observable", "Maybe", "Single", "Completable", "ConnectableObservable", "Disposable", "Iterable", "Stream", "Future", "CompletionStage"); + aOrAn(e, m, "Observable"); - missingClosingDD(e, m, "Observable"); + missingClosingDD(e, m, "Observable", "io.reactivex.rxjava3.core"); backpressureMentionedWithoutAnnotation(e, m, "Observable"); } } @@ -443,8 +630,8 @@ public void singleDocRefersToSingleTypes() throws Exception { && !m.signature.contains("Flowable") && !m.signature.contains("Observable") && !m.signature.contains("ObservableSource")) { - e.append("java.lang.RuntimeException: Single doc mentions onNext but no Flowable/Observable in signature\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions onNext but no Flowable/Observable in signature\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -459,8 +646,8 @@ public void singleDocRefersToSingleTypes() throws Exception { if (!m.signature.contains("Publisher") && !m.signature.contains("Flowable") && !m.signature.contains("TestSubscriber")) { - e.append("java.lang.RuntimeException: Single doc mentions Subscriber but not using Flowable\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Subscriber but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -475,8 +662,8 @@ public void singleDocRefersToSingleTypes() throws Exception { if (!m.signature.contains("Flowable") && !m.signature.contains("Publisher") ) { - e.append("java.lang.RuntimeException: Single doc mentions Subscription but not using Flowable\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Subscription but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -493,8 +680,8 @@ public void singleDocRefersToSingleTypes() throws Exception { && !m.signature.contains("TestObserver")) { if (idx < 6 || !m.javadoc.substring(idx - 6, idx + 8).equals("SingleObserver")) { - e.append("java.lang.RuntimeException: Single doc mentions Observer but not using Observable\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Observer but not using Observable\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -509,8 +696,8 @@ public void singleDocRefersToSingleTypes() throws Exception { if (idx >= 0) { if (!m.signature.contains("Publisher")) { if (idx == 0 || !m.javadoc.substring(idx - 1, idx + 9).equals("(Publisher")) { - e.append("java.lang.RuntimeException: Single doc mentions Publisher but not in the signature\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Publisher but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -524,8 +711,8 @@ public void singleDocRefersToSingleTypes() throws Exception { int idx = m.javadoc.indexOf(" Flowable", jdx); if (idx >= 0) { if (!m.signature.contains("Flowable")) { - e.append("java.lang.RuntimeException: Single doc mentions Flowable but not in the signature\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Flowable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -537,8 +724,8 @@ public void singleDocRefersToSingleTypes() throws Exception { int idx = m.javadoc.indexOf(" Maybe", jdx); if (idx >= 0) { if (!m.signature.contains("Maybe")) { - e.append("java.lang.RuntimeException: Single doc mentions Maybe but not in the signature\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Maybe but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -550,8 +737,8 @@ public void singleDocRefersToSingleTypes() throws Exception { int idx = m.javadoc.indexOf(" MaybeSource", jdx); if (idx >= 0) { if (!m.signature.contains("MaybeSource")) { - e.append("java.lang.RuntimeException: Single doc mentions SingleSource but not in the signature\r\n at io.reactivex.") - .append("Maybe (Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions SingleSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Maybe.method(Maybe.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -563,8 +750,8 @@ public void singleDocRefersToSingleTypes() throws Exception { int idx = m.javadoc.indexOf(" Observable", jdx); if (idx >= 0) { if (!m.signature.contains("Observable")) { - e.append("java.lang.RuntimeException: Single doc mentions Observable but not in the signature\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions Observable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -576,8 +763,8 @@ public void singleDocRefersToSingleTypes() throws Exception { int idx = m.javadoc.indexOf(" ObservableSource", jdx); if (idx >= 0) { if (!m.signature.contains("ObservableSource")) { - e.append("java.lang.RuntimeException: Single doc mentions ObservableSource but not in the signature\r\n at io.reactivex.") - .append("Single (Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Single doc mentions ObservableSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Single.method(Single.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; } else { @@ -585,8 +772,10 @@ public void singleDocRefersToSingleTypes() throws Exception { } } + checkAtReturnAndSignatureMatch("Single", m, e, "Flowable", "Observable", "Maybe", "Single", "Completable", "Disposable", "Iterable", "Stream", "Future", "CompletionStage"); + aOrAn(e, m, "Single"); - missingClosingDD(e, m, "Single"); + missingClosingDD(e, m, "Single", "io.reactivex.rxjava3.core"); backpressureMentionedWithoutAnnotation(e, m, "Single"); } } @@ -617,8 +806,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { && !m.signature.contains("Flowable") && !m.signature.contains("Observable") && !m.signature.contains("ObservableSource")) { - e.append("java.lang.RuntimeException: Completable doc mentions onNext but no Flowable/Observable in signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions onNext but no Flowable/Observable in signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -633,8 +822,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("Publisher") && !m.signature.contains("Flowable") && !m.signature.contains("TestSubscriber")) { - e.append("java.lang.RuntimeException: Completable doc mentions Subscriber but not using Flowable\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Subscriber but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -649,8 +838,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("Flowable") && !m.signature.contains("Publisher") ) { - e.append("java.lang.RuntimeException: Completable doc mentions Subscription but not using Flowable\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Subscription but not using Flowable\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } jdx = idx + 6; @@ -667,8 +856,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { && !m.signature.contains("TestObserver")) { if (idx < 11 || !m.javadoc.substring(idx - 11, idx + 8).equals("CompletableObserver")) { - e.append("java.lang.RuntimeException: Completable doc mentions Observer but not using Observable\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Observer but not using Observable\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -683,8 +872,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (idx >= 0) { if (!m.signature.contains("Publisher")) { if (idx == 0 || !m.javadoc.substring(idx - 1, idx + 9).equals("(Publisher")) { - e.append("java.lang.RuntimeException: Completable doc mentions Publisher but not in the signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Publisher but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } @@ -700,8 +889,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("Flowable")) { Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*Flowable"); if (!p.matcher(m.javadoc).find()) { - e.append("java.lang.RuntimeException: Completable doc mentions Flowable but not in the signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Flowable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } jdx = idx + 6; @@ -716,8 +905,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("Single")) { Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*Single"); if (!p.matcher(m.javadoc).find()) { - e.append("java.lang.RuntimeException: Completable doc mentions Single but not in the signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Single but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } jdx = idx + 6; @@ -732,8 +921,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("SingleSource")) { Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*SingleSource"); if (!p.matcher(m.javadoc).find()) { - e.append("java.lang.RuntimeException: Completable doc mentions SingleSource but not in the signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions SingleSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } jdx = idx + 6; @@ -748,8 +937,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("Observable")) { Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*Observable"); if (!p.matcher(m.javadoc).find()) { - e.append("java.lang.RuntimeException: Completable doc mentions Observable but not in the signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions Observable but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } jdx = idx + 6; @@ -764,8 +953,8 @@ public void completableDocRefersToCompletableTypes() throws Exception { if (!m.signature.contains("ObservableSource")) { Pattern p = Pattern.compile("@see\\s+#[A-Za-z0-9 _.,()]*ObservableSource"); if (!p.matcher(m.javadoc).find()) { - e.append("java.lang.RuntimeException: Completable doc mentions ObservableSource but not in the signature\r\n at io.reactivex.") - .append("Completable (Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); + e.append("java.lang.RuntimeException: Completable doc mentions ObservableSource but not in the signature\r\n at io.reactivex.rxjava3.core.") + .append("Completable.method(Completable.java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); } } jdx = idx + 6; @@ -773,8 +962,11 @@ public void completableDocRefersToCompletableTypes() throws Exception { break; } } + + checkAtReturnAndSignatureMatch("Completable", m, e, "Flowable", "Observable", "Maybe", "Single", "Completable", "Disposable", "Iterable", "Stream", "Future", "CompletionStage"); + aOrAn(e, m, "Completable"); - missingClosingDD(e, m, "Completable"); + missingClosingDD(e, m, "Completable", "io.reactivex.rxjava3.core"); backpressureMentionedWithoutAnnotation(e, m, "Completable"); } } @@ -786,6 +978,38 @@ public void completableDocRefersToCompletableTypes() throws Exception { } } + static void checkAtReturnAndSignatureMatch(String className, RxMethod m, StringBuilder e, String... types) { + for (String t : types) { + String regex; + if (t.contains("Completable")) { + regex = "(?s).*?\\s" + t + "\\s+\\w+\\(.*"; + } else { + regex = "(?s).*?\\s" + t + "\\<.*?\\>\\s+\\w+\\(.*"; + } + if (m.signature.matches(regex)) { + for (String at : AT_RETURN_WORDS) { + for (String u : types) { + if (!t.equals(u)) { + int idx = m.javadoc.indexOf(at + "{@code " + u); + if (idx >= 0) { + e.append("Returns ").append(t) + .append(" but docs return ") + .append(u) + .append("\r\n at io.reactivex.rxjava3.core.") + .append(className) + .append(".method(") + .append(className) + .append(".java:") + .append(m.javadocLine + lineNumber(m.javadoc, idx) - 1) + .append(")\r\n\r\n"); + } + } + } + } + } + } + } + static void aOrAn(StringBuilder e, RxMethod m, String baseTypeName) { aOrAn(e, m, " an", "Single", baseTypeName); aOrAn(e, m, " an", "Maybe", baseTypeName); @@ -807,9 +1031,9 @@ static void aOrAn(StringBuilder e, RxMethod m, String wrongPre, String word, Str if (idx >= 0) { e.append("java.lang.RuntimeException: a/an typo ") .append(word) - .append("\r\n at io.reactivex.") + .append("\r\n at io.reactivex.rxjava3.core.") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); jdx = idx + 6; @@ -824,9 +1048,9 @@ static void aOrAn(StringBuilder e, RxMethod m, String wrongPre, String word, Str if (idx >= 0) { e.append("java.lang.RuntimeException: a/an typo ") .append(word) - .append("\r\n at io.reactivex.") + .append("\r\n at io.reactivex.rxjava3.core.") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); jdx = idx + 6; @@ -841,9 +1065,9 @@ static void aOrAn(StringBuilder e, RxMethod m, String wrongPre, String word, Str if (idx >= 0) { e.append("java.lang.RuntimeException: a/an typo ") .append(word) - .append("\r\n at io.reactivex.") + .append("\r\n at io.reactivex.rxjava3.core.") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); jdx = idx + 6; @@ -858,9 +1082,9 @@ static void aOrAn(StringBuilder e, RxMethod m, String wrongPre, String word, Str if (idx >= 0) { e.append("java.lang.RuntimeException: a/an typo ") .append(word) - .append("\r\n at io.reactivex.") + .append("\r\n at io.reactivex.rxjava3.core.") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.javadocLine + lineNumber(m.javadoc, idx) - 1).append(")\r\n\r\n"); jdx = idx + 6; @@ -895,9 +1119,9 @@ static void aOrAn(StringBuilder e, RxMethod m, String wrongPre, String word, Str if (idx >= 0) { e.append("java.lang.RuntimeException: a/an typo ") .append(word) - .append("\r\n at io.reactivex.") + .append("\r\n at io.reactivex.rxjava3.core.") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.javadocLine).append(")\r\n\r\n"); jdx = idx + wrongPre.length() + 1 + word.length(); @@ -907,7 +1131,7 @@ static void aOrAn(StringBuilder e, RxMethod m, String wrongPre, String word, Str } } - static void missingClosingDD(StringBuilder e, RxMethod m, String baseTypeName) { + static void missingClosingDD(StringBuilder e, RxMethod m, String baseTypeName, String packageName) { int jdx = 0; for (;;) { int idx1 = m.javadoc.indexOf("

    ", jdx); @@ -923,9 +1147,11 @@ static void missingClosingDD(StringBuilder e, RxMethod m, String baseTypeName) { jdx = idx2 + 5; } else { e.append("java.lang.RuntimeException: unbalanced
    ") - .append("\r\n at io.reactivex.") + .append("\r\n at ") + .append(packageName) + .append(".") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.javadocLine + lineNumber(m.javadoc, idx1) - 1).append(")\r\n\r\n"); break; @@ -936,11 +1162,13 @@ static void missingClosingDD(StringBuilder e, RxMethod m, String baseTypeName) { static void backpressureMentionedWithoutAnnotation(StringBuilder e, RxMethod m, String baseTypeName) { if (m.backpressureDocLine > 0 && m.backpressureKind == null) { e.append("java.lang.RuntimeException: backpressure documented but not annotated ") - .append("\r\n at io.reactivex.") + .append("\r\n at io.reactivex.rxjava3.core.") .append(baseTypeName) - .append(" (") + .append(".method(") .append(baseTypeName) .append(".java:").append(m.backpressureDocLine).append(")\r\n\r\n"); } } + + static final String[] AT_RETURN_WORDS = { "@return a ", "@return an ", "@return the new ", "@return a new " }; } diff --git a/src/test/java/io/reactivex/rxjava3/validators/MaybeNo2Dot0Since.java b/src/test/java/io/reactivex/rxjava3/validators/MaybeNo2Dot0Since.java index f8b9e63164..b894492894 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/MaybeNo2Dot0Since.java +++ b/src/test/java/io/reactivex/rxjava3/validators/MaybeNo2Dot0Since.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/validators/NewLinesBeforeAnnotation.java b/src/test/java/io/reactivex/rxjava3/validators/NewLinesBeforeAnnotation.java index 1e4177c4cd..2c27fc6fd4 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/NewLinesBeforeAnnotation.java +++ b/src/test/java/io/reactivex/rxjava3/validators/NewLinesBeforeAnnotation.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -72,7 +72,7 @@ static void findPattern(int newLines) throws Exception { return; } - Queue dirs = new ArrayDeque(); + Queue dirs = new ArrayDeque<>(); StringBuilder fail = new StringBuilder(); fail.append("The following code pattern was found: "); @@ -102,7 +102,7 @@ static void findPattern(int newLines) throws Exception { String fname = u.getName(); if (fname.endsWith(".java")) { - List lines = new ArrayList(); + List lines = new ArrayList<>(); BufferedReader in = new BufferedReader(new FileReader(u)); try { for (;;) { diff --git a/src/test/java/io/reactivex/rxjava3/validators/NoAnonymousInnerClassesTest.java b/src/test/java/io/reactivex/rxjava3/validators/NoAnonymousInnerClassesTest.java index d744c2a48c..61db410b5b 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/NoAnonymousInnerClassesTest.java +++ b/src/test/java/io/reactivex/rxjava3/validators/NoAnonymousInnerClassesTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ public void verify() throws Exception { StringBuilder b = new StringBuilder("Anonymous inner classes found:"); - Queue queue = new ArrayDeque(); + Queue queue = new ArrayDeque<>(); queue.offer(f); diff --git a/src/test/java/io/reactivex/rxjava3/validators/NonNullMethodTypeArgumentCheck.java b/src/test/java/io/reactivex/rxjava3/validators/NonNullMethodTypeArgumentCheck.java new file mode 100644 index 0000000000..e8c4b81715 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/NonNullMethodTypeArgumentCheck.java @@ -0,0 +1,149 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import static org.junit.Assert.assertEquals; + +import java.io.*; +import java.nio.file.Files; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Verify static methods and final methods declaring type arguments + * declare {@code @NonNull} for said argument. + * + */ +public class NonNullMethodTypeArgumentCheck { + + static void process(Class clazz) { + + String className = clazz.getSimpleName(); + String parentPackage = clazz.getPackage().getName(); + + StringBuilder result = new StringBuilder(); + int count = 0; + + try { + File f = TestHelper.findSource(className, parentPackage); + + try (BufferedReader in = Files.newBufferedReader(f.toPath())) { + int lineCount = 1; + String line = null; + + while ((line = in.readLine()) != null) { + line = line.trim(); + + if (!line.contains(" to(")) { + if (line.startsWith("public static <") || line.startsWith("public final <")) { + + for (String ta : parseTypeArguments(line)) { + if (!ta.startsWith("@NonNull") && !ta.startsWith("@Nullable")) { + if (!("Maybe".equals(clazz.getSimpleName()) && (line.contains("fromCallable(") || line.contains("fromSupplier(")))) { + result.append("Missing annotation on argument ").append(ta).append("\r\nat ") + .append(parentPackage).append(".").append(className).append(".method(") + .append(className).append(".java:").append(lineCount).append(")\r\n"); + count++; + } + } + } + } + } + lineCount++; + } + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + if (count != 0) { + throw new IllegalArgumentException("Found " + count + " cases\r\n" + result.toString()); + } + } + + static List parseTypeArguments(String line) { + List result = new ArrayList<>(); + int offset = line.indexOf("<"); + int c = 1; + int i = offset + 1; + int j = i; + for (; i < line.length(); i++) { + if (line.charAt(i) == '<') { + c++; + } else + if (line.charAt(i) == '>') { + c--; + if (c == 0) { + break; + } + } else + if (line.charAt(i) == ',' && c == 1) { + result.add(line.substring(j, i).trim()); + j = i + 1; + } + } + result.add(line.substring(j, i).trim()); + return result; + } + + @Test + public void parseTypeArguments() { + assertEquals(new ArrayList<>(Arrays.asList("T")), parseTypeArguments("")); + assertEquals(new ArrayList<>(Arrays.asList("T", "U")), parseTypeArguments("")); + assertEquals(new ArrayList<>(Arrays.asList("T", "Flowable")), parseTypeArguments(">")); + assertEquals(new ArrayList<>(Arrays.asList("T", "Flowable")), parseTypeArguments(">")); + } + + @Test + public void flowable() { + process(Flowable.class); + } + + @Test + public void observable() { + process(Observable.class); + } + + @Test + public void maybe() { + process(Maybe.class); + } + + @Test + public void single() { + process(Single.class); + } + + @Test + public void completable() { + process(Completable.class); + } + + @Test + public void parallel() { + process(ParallelFlowable.class); + } + + @Test + public void plugins() { + process(RxJavaPlugins.class); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/OperatorsAreFinal.java b/src/test/java/io/reactivex/rxjava3/validators/OperatorsAreFinal.java index 6343a6e02b..a59e3aefa2 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/OperatorsAreFinal.java +++ b/src/test/java/io/reactivex/rxjava3/validators/OperatorsAreFinal.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java b/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java new file mode 100644 index 0000000000..76d4a836f9 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/OperatorsUseInterfaces.java @@ -0,0 +1,189 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import static org.junit.Assert.*; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.Callable; + +import org.junit.Test; +import org.reactivestreams.Publisher; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.functions.*; +import io.reactivex.rxjava3.parallel.ParallelFlowable; + +/** + * Verify that an operator method uses base interfaces as its direct input or + * has lambdas returning base interfaces. + */ +public class OperatorsUseInterfaces { + + @Test + public void checkFlowable() { + checkClass(Flowable.class); + } + + @Test + public void checkObservable() { + checkClass(Observable.class); + } + + @Test + public void checkMaybe() { + checkClass(Maybe.class); + } + + @Test + public void checkSingle() { + checkClass(Single.class); + } + + @Test + public void checkCompletable() { + checkClass(Completable.class); + } + + @Test + public void checkParallelFlowable() { + checkClass(ParallelFlowable.class); + } + + void checkClass(Class clazz) { + StringBuilder error = new StringBuilder(); + int errors = 0; + + for (Method method : clazz.getMethods()) { + if (method.getDeclaringClass() == clazz) { + int pidx = 1; + for (Parameter param : method.getParameters()) { + Class type = param.getType(); + if (type.isArray()) { + type = type.getComponentType(); + } + if (CLASSES.contains(type)) { + errors++; + error.append("Non-interface input parameter #") + .append(pidx) + .append(": ") + .append(type) + .append("\r\n") + .append(" ") + .append(method) + .append("\r\n") + ; + } + if (CAN_RETURN.contains(type)) { + Type gtype = method.getGenericParameterTypes()[pidx - 1]; + if (gtype instanceof GenericArrayType) { + gtype = ((GenericArrayType)gtype).getGenericComponentType(); + } + ParameterizedType ptype = (ParameterizedType)gtype; + for (;;) { + Type[] parameterArgTypes = ptype.getActualTypeArguments(); + Type argType = parameterArgTypes[parameterArgTypes.length - 1]; + if (argType instanceof GenericArrayType) { + argType = ((GenericArrayType)argType).getGenericComponentType(); + } + if (argType instanceof ParameterizedType) { + ParameterizedType lastArg = (ParameterizedType)argType; + + if (CLASSES.contains(lastArg.getRawType())) { + errors++; + error.append("Non-interface lambda return #") + .append(pidx) + .append(": ") + .append(type) + .append("\r\n") + .append(" ") + .append(method) + .append("\r\n") + ; + } + + if (CAN_RETURN.contains(lastArg.getRawType())) { + ptype = lastArg; + continue; + } + } + break; + } + } + pidx++; + } + } + } + + if (errors != 0) { + error.insert(0, "Found " + errors + " issues\r\n"); + fail(error.toString()); + } + } + + public void method1(Flowable f) { + // self-test + } + + public void method2(Callable> c) { + // self-test + } + + public void method3(Supplier>> c) { + // self-test + } + + public void method4(Flowable[] array) { + // self-test + } + + public void method5(Callable[]> c) { + // self-test + } + + public void method6(Callable[]>> c) { + // self-test + } + + @Test + public void checkSelf() { + try { + checkClass(OperatorsUseInterfaces.class); + throw new RuntimeException("Should have failed"); + } catch (AssertionError expected) { + assertTrue(expected.toString(), expected.toString().contains("method1")); + assertTrue(expected.toString(), expected.toString().contains("method2")); + assertTrue(expected.toString(), expected.toString().contains("method3")); + assertTrue(expected.toString(), expected.toString().contains("method4")); + assertTrue(expected.toString(), expected.toString().contains("method5")); + assertTrue(expected.toString(), expected.toString().contains("method6")); + } + } + + static final Set> CLASSES = new HashSet<>(Arrays.asList( + Flowable.class, Observable.class, + Maybe.class, Single.class, + Completable.class + )); + + static final Set> CAN_RETURN = new HashSet<>(Arrays.asList( + Callable.class, Supplier.class, + Function.class, BiFunction.class, Function3.class, Function4.class, + Function5.class, Function6.class, Function7.class, Function8.class, + Function9.class, + Publisher.class, ObservableSource.class, MaybeSource.class, SingleSource.class + )); +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java b/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java index d8b5d14f7e..c2ca87d4d2 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java +++ b/src/test/java/io/reactivex/rxjava3/validators/ParamValidationCheckerTest.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -14,8 +14,10 @@ package io.reactivex.rxjava3.validators; import java.lang.reflect.*; +import java.time.Duration; import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; import org.junit.Test; import org.reactivestreams.*; @@ -23,7 +25,7 @@ import io.reactivex.rxjava3.core.*; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.*; import io.reactivex.rxjava3.exceptions.TestException; import io.reactivex.rxjava3.functions.*; import io.reactivex.rxjava3.internal.functions.Functions; @@ -80,7 +82,7 @@ public void checkParallelFlowable() { static Map, List> defaultInstances; static { - overrides = new HashMap>(); + overrides = new HashMap<>(); // *********************************************************************************************************************** @@ -91,7 +93,7 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "elementAtOrError", Long.TYPE)); // negative skip count is ignored - addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skip", Long.TYPE)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "skip", Long.TYPE)); // negative skip time is considered as zero skip time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class, Scheduler.class)); @@ -123,10 +125,6 @@ public void checkParallelFlowable() { // negative timeout is allowed addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class)); - addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class, Scheduler.class)); - - // null default is allowed - addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "blockingLast", Object.class)); // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class)); @@ -144,22 +142,18 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); - // null default is allowed - addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "blockingMostRecent", Object.class)); - // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class, Scheduler.class)); - // null default is allowed - addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "blockingFirst", Object.class)); - // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // null Action allowed addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "onBackpressureBuffer", Long.TYPE, Action.class, BackpressureOverflowStrategy.class)); + addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "onBackpressureBuffer", Long.TYPE, Action.class, BackpressureOverflowStrategy.class, Consumer.class)); // zero repeat is allowed addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE)); @@ -185,6 +179,7 @@ public void checkParallelFlowable() { // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "take", Long.TYPE, TimeUnit.class)); @@ -198,6 +193,7 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Boolean.TYPE)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class)); @@ -228,16 +224,19 @@ public void checkParallelFlowable() { // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Scheduler.class)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Boolean.TYPE)); addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Consumer.class)); // negative buffer time is considered as zero buffer time addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class)); @@ -297,6 +296,8 @@ public void checkParallelFlowable() { // negative time is considered as zero time addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Boolean.TYPE)); + addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); // zero repeat is allowed addOverride(new ParamOverride(Maybe.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE)); @@ -323,7 +324,6 @@ public void checkParallelFlowable() { // negative timeout is allowed addOverride(new ParamOverride(Single.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class)); - addOverride(new ParamOverride(Single.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class, Scheduler.class)); // negative time is considered as zero time addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class)); @@ -351,7 +351,7 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "elementAtOrError", Long.TYPE)); // negative skip count is ignored - addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skip", Long.TYPE)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "skip", Long.TYPE)); // negative skip time is considered as zero skip time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class, Scheduler.class)); @@ -379,10 +379,6 @@ public void checkParallelFlowable() { // negative timeout is allowed addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class)); - addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class, Scheduler.class)); - - // null default is allowed - addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "blockingLast", Object.class)); // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class)); @@ -400,19 +396,14 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); - // null default is allowed - addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "blockingMostRecent", Object.class)); - // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class, Scheduler.class)); - // null default is allowed - addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "blockingFirst", Object.class)); - // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // zero repeat is allowed addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE)); @@ -438,6 +429,7 @@ public void checkParallelFlowable() { // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "take", Long.TYPE, TimeUnit.class)); @@ -451,6 +443,7 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Boolean.TYPE)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class)); @@ -481,16 +474,19 @@ public void checkParallelFlowable() { // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class, Scheduler.class)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class, Scheduler.class, Consumer.class)); // negative time is considered as zero time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Scheduler.class)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Boolean.TYPE)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLatest", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Consumer.class)); // negative buffer time is considered as zero buffer time addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class)); @@ -501,9 +497,22 @@ public void checkParallelFlowable() { addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE, Boolean.TYPE)); addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE, Boolean.TYPE, Integer.TYPE)); + // null value allowed + + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "firstStage", Object.class)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "singleStage", Object.class)); + addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "lastStage", Object.class)); + + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "firstStage", Object.class)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "singleStage", Object.class)); + addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "lastStage", Object.class)); + + addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "toCompletionStage", Object.class)); + addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "toCompletionStage", Object.class)); + // ----------------------------------------------------------------------------------- - ignores = new HashMap>(); + ignores = new HashMap<>(); // needs special param validation due to (long)start + end - 1 <= Integer.MAX_VALUE addIgnore(new ParamIgnore(Flowable.class, "range", Integer.TYPE, Integer.TYPE)); @@ -533,7 +542,7 @@ public void checkParallelFlowable() { // ----------------------------------------------------------------------------------- - defaultValues = new HashMap, Object>(); + defaultValues = new HashMap<>(); defaultValues.put(Publisher.class, new NeverPublisher()); defaultValues.put(Flowable.class, new NeverPublisher()); @@ -564,6 +573,7 @@ public void checkParallelFlowable() { for (Class interfaces : AllFunctionals.class.getInterfaces()) { defaultValues.put(interfaces, af); } + defaultValues.put(Subscriber.class, af); defaultValues.put(TimeUnit.class, TimeUnit.SECONDS); defaultValues.put(Scheduler.class, Schedulers.single()); defaultValues.put(BackpressureStrategy.class, BackpressureStrategy.MISSING); @@ -584,6 +594,16 @@ public void checkParallelFlowable() { defaultValues.put(ParallelFailureHandling.class, ParallelFailureHandling.ERROR); + defaultValues.put(DisposableContainer.class, new CompositeDisposable()); + + // JDK 8 types + + defaultValues.put(Optional.class, Optional.of(1)); + defaultValues.put(CompletionStage.class, CompletableFuture.completedFuture(1)); + defaultValues.put(Stream.class, Stream.of(1, 2, 3)); + defaultValues.put(Duration.class, Duration.ofSeconds(1)); + defaultValues.put(Collector.class, Collectors.toList()); + @SuppressWarnings("rawtypes") class MixedConverters implements FlowableConverter, ObservableConverter, SingleConverter, MaybeConverter, CompletableConverter, ParallelFlowableConverter { @@ -626,7 +646,7 @@ public Object apply(Flowable upstream) { // ----------------------------------------------------------------------------------- - defaultInstances = new HashMap, List>(); + defaultInstances = new HashMap<>(); // addDefaultInstance(Flowable.class, Flowable.empty(), "Empty()"); // addDefaultInstance(Flowable.class, Flowable.empty().hide(), "Empty().Hide()"); @@ -658,7 +678,7 @@ static void addIgnore(ParamIgnore ignore) { String key = ignore.toString(); List list = ignores.get(key); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); ignores.put(key, list); } list.add(ignore); @@ -668,7 +688,7 @@ static void addOverride(ParamOverride ignore) { String key = ignore.toString(); List list = overrides.get(key); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); overrides.put(key, list); } list.add(ignore); @@ -677,7 +697,7 @@ static void addOverride(ParamOverride ignore) { static void addDefaultInstance(Class clazz, Object o, String tag) { List list = defaultInstances.get(clazz); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); defaultInstances.put(clazz, list); } list.add(o); @@ -767,7 +787,7 @@ void checkClass(Class clazz) { List overrideList = overrides.get(key); - List baseObjects = new ArrayList(); + List baseObjects = new ArrayList<>(); if ((m.getModifiers() & Modifier.STATIC) != 0) { baseObjects.add(null); @@ -834,7 +854,7 @@ void checkClass(Class clazz) { } } - List entryValues = new ArrayList(); + List entryValues = new ArrayList<>(); if (entryClass.isPrimitive()) { addCheckPrimitive(params[i], overrideEntry, entryValues); @@ -870,6 +890,12 @@ void checkClass(Class clazz) { error = ex; } + if (!success && error.getCause() instanceof NullPointerException) { + if (!error.getCause().toString().contains("is null")) { + fail++; + b.append("\r\nNPEs should indicate which argument failed: " + m + " # " + i + " = " + p + ", tag = " + tag + ", params = " + Arrays.toString(callParams2)); + } + } if (success != shouldSucceed) { fail++; if (shouldSucceed) { @@ -914,7 +940,7 @@ static final class AllFunctionals Function3, Function4, Function5, Function6, Function7, Function8, Function9, FlowableOnSubscribe, ObservableOnSubscribe, SingleOnSubscribe, MaybeOnSubscribe, CompletableOnSubscribe, FlowableTransformer, ObservableTransformer, SingleTransformer, MaybeTransformer, CompletableTransformer, - Subscriber, FlowableSubscriber, Observer, SingleObserver, MaybeObserver, CompletableObserver, + FlowableSubscriber, Observer, SingleObserver, MaybeObserver, CompletableObserver, FlowableOperator, ObservableOperator, SingleOperator, MaybeOperator, CompletableOperator, Comparator, ParallelTransformer { @@ -1199,4 +1225,4 @@ public String toString() { return "NeverCompletable"; } } -} +} \ No newline at end of file diff --git a/src/test/java/io/reactivex/rxjava3/validators/ParamValidationNaming.java b/src/test/java/io/reactivex/rxjava3/validators/ParamValidationNaming.java new file mode 100644 index 0000000000..1928fbee60 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/ParamValidationNaming.java @@ -0,0 +1,542 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import java.io.File; +import java.nio.file.Files; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.*; +import io.reactivex.rxjava3.flowables.ConnectableFlowable; +import io.reactivex.rxjava3.observables.ConnectableObservable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Check if the parameter name in Objects.requireNonNull + * and ObjectHelper.verifyPositive calls match the parameter + * name in the message. + */ +public class ParamValidationNaming { + + @Test + public void checkCompletable() throws Exception { + processFile(Completable.class); + } + + @Test + public void checkSingle() throws Exception { + processFile(Single.class); + } + + @Test + public void checkMaybe() throws Exception { + processFile(Maybe.class); + } + + @Test + public void checkObservable() throws Exception { + processFile(Observable.class); + } + + @Test + public void checkFlowable() throws Exception { + processFile(Flowable.class); + } + + @Test + public void checkParallelFlowable() throws Exception { + processFile(ParallelFlowable.class); + } + + @Test + public void checkConnectableObservable() throws Exception { + processFile(ConnectableObservable.class); + } + + @Test + public void checkConnectableFlowable() throws Exception { + processFile(ConnectableFlowable.class); + } + + @Test + public void checkSubject() throws Exception { + processFile(Subject.class); + } + + @Test + public void checkFlowableProcessor() throws Exception { + processFile(FlowableProcessor.class); + } + + @Test + public void checkDisposable() throws Exception { + processFile(Disposable.class); + } + + @Test + public void checkScheduler() throws Exception { + processFile(Scheduler.class); + } + + @Test + public void checkSchedulers() throws Exception { + processFile(Schedulers.class); + } + + @Test + public void checkAsyncSubject() throws Exception { + processFile(AsyncSubject.class); + } + + @Test + public void checkBehaviorSubject() throws Exception { + processFile(BehaviorSubject.class); + } + + @Test + public void checkPublishSubject() throws Exception { + processFile(PublishSubject.class); + } + + @Test + public void checkReplaySubject() throws Exception { + processFile(ReplaySubject.class); + } + + @Test + public void checkUnicastSubject() throws Exception { + processFile(UnicastSubject.class); + } + + @Test + public void checkSingleSubject() throws Exception { + processFile(SingleSubject.class); + } + + @Test + public void checkMaybeSubject() throws Exception { + processFile(MaybeSubject.class); + } + + @Test + public void checkCompletableSubject() throws Exception { + processFile(CompletableSubject.class); + } + + @Test + public void checkAsyncProcessor() throws Exception { + processFile(AsyncProcessor.class); + } + + @Test + public void checkBehaviorProcessor() throws Exception { + processFile(BehaviorProcessor.class); + } + + @Test + public void checkPublishProcessor() throws Exception { + processFile(PublishProcessor.class); + } + + @Test + public void checkReplayProcessor() throws Exception { + processFile(ReplayProcessor.class); + } + + @Test + public void checkUnicastProcessor() throws Exception { + processFile(UnicastProcessor.class); + } + + @Test + public void checkMulticastProcessor() throws Exception { + processFile(MulticastProcessor.class); + } + + @Test + public void checkCompositeDisposable() throws Exception { + processFile(CompositeDisposable.class); + } + + static void processFile(Class clazz) throws Exception { + String baseClassName = clazz.getSimpleName(); + File f = TestHelper.findSource(baseClassName, clazz.getPackage().getName()); + if (f == null) { + return; + } + String fullClassName = clazz.getName(); + + int errorCount = 0; + StringBuilder errors = new StringBuilder(); + + List lines = Files.readAllLines(f.toPath()); + + for (int j = 0; j < lines.size(); j++) { + String line = lines.get(j).trim(); + + for (ValidatorStrings validatorStr : VALIDATOR_STRINGS) { + int strIdx = line.indexOf(validatorStr.code); + if (strIdx >= 0) { + + int comma = line.indexOf(',', strIdx + validatorStr.code.length()); + + String paramName = line.substring(strIdx + validatorStr.code.length(), comma); + + int quote = line.indexOf('"', comma); + + String message = line.substring(quote + 1, Math.min(line.length(), quote + 2 + paramName.length())); + + if (line.contains("\"A Disposable")) { + continue; + } + + if (!line.contains("\"The RxJavaPlugins") + && !(message.startsWith(paramName) + && (message.endsWith(" ") || message.endsWith("\"")))) { + errorCount++; + errors.append("L") + .append(j) + .append(" : Wrong validator message parameter name\r\n ") + .append(line) + .append("\r\n") + .append(" ").append(paramName).append(" != ").append(message) + .append("\r\n at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + + int midx = j - 1; + // find the method declaration + for (; midx >= 0; midx--) { + String linek = lines.get(midx).trim(); + if (linek.startsWith("public") || linek.startsWith("private") + || linek.startsWith("protected") + || linek.startsWith("static") + || linek.startsWith(baseClassName)) { + break; + } + } + + if (line.contains("\"The RxJavaPlugins")) { + continue; + } + + // find JavaDoc of throws + boolean found = false; + for (int k = midx - 1; k >= 0; k--) { + String linek = lines.get(k).trim(); + if (linek.startsWith("/**")) { + break; + } + if (linek.startsWith("}")) { + found = true; // no method JavaDoc present + break; + } + if (linek.startsWith(validatorStr.javadoc)) { + // see if a @code paramName is present + String paramStr = "{@code " + paramName + "}"; + for (int m = k; m < lines.size(); m++) { + String linem = lines.get(m).trim(); + if (linem.startsWith("* @see") + || linem.startsWith("* @since") + || linem.startsWith("*/")) { + break; + } + if (linem.contains(paramStr)) { + found = true; + break; + } + } + break; + } + } + + if (!found) { + errorCount++; + errors.append("L") + .append(j) + .append(" : missing '") + .append(validatorStr.javadoc) + .append("' for argument validation: ") + .append(paramName) + .append("\r\n ") + .append(line) + .append("\r\n at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + } + + for (ValidatorStrings validatorStr : EXCEPTION_STRINGS) { + int strIdx = line.indexOf(validatorStr.code); + if (strIdx >= 0) { + + int midx = j - 1; + // find the method declaration + for (; midx >= 0; midx--) { + String linek = lines.get(midx).trim(); + if (linek.startsWith("public") || linek.startsWith("private") + || linek.startsWith("protected") + || linek.startsWith("static") + || linek.startsWith(baseClassName)) { + break; + } + } + + // find JavaDoc of throws + boolean found = false; + for (int k = midx - 1; k >= 0; k--) { + String linek = lines.get(k).trim(); + if (linek.startsWith("/**")) { + break; + } + if (linek.startsWith("}")) { + found = true; // no JavaDoc + break; + } + if (linek.startsWith(validatorStr.javadoc)) { + found = true; + } + } + + if (!found) { + errorCount++; + errors.append("L") + .append(j) + .append(" : missing '") + .append(validatorStr.javadoc) + .append("' for exception\r\n ") + .append(line) + .append("\r\n at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + } + + if (line.startsWith("public") || line.startsWith("protected") || line.startsWith("final") || line.startsWith("private") + || line.startsWith("static")) { + for (ValidatorStrings validatorStr : TYPICAL_ARGUMENT_STRINGS) { + // find the method declaration ending { + for (int i = j; i < lines.size(); i++) { + String linei = lines.get(i).trim(); + + // space + code for capturing type declarations + String varPattern = " " + validatorStr.code; + if (linei.contains(varPattern + ")") + || linei.contains(varPattern + ",") + || linei.endsWith(varPattern)) { + // ignore nullable-annotated arguments + if (!linei.matches(".*\\@Nullable\\s.*" + validatorStr.code + ".*")) { + boolean found = false; + for (int k = i - 1; k >= 0; k--) { + String linek = lines.get(k).trim(); + if (linek.startsWith("/**")) { + break; + } + if (linek.startsWith("}")) { + found = true; // no method JavaDoc present + break; + } + if (linek.startsWith(validatorStr.javadoc)) { + // see if a @code paramName is present + String paramStr = "{@code " + validatorStr.code + "}"; + for (int m = k; m < lines.size(); m++) { + String linem = lines.get(m).trim(); + if (linem.startsWith("* @see") + || linem.startsWith("* @since") + || linem.startsWith("*/")) { + break; + } + if (linem.contains(paramStr)) { + found = true; + break; + } + } + break; + } + } + + if (!found) { + errorCount++; + errors.append("L") + .append(j) + .append(" : missing '") + .append(validatorStr.javadoc) + .append("' for typical argument: ") + .append(validatorStr.code) + .append("\r\n ") + .append(line) + .append("\r\n at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + } + + if (linei.endsWith("{") || linei.endsWith(";")) { + break; + } + } + } + } + } + + if (errorCount != 0) { + errors.insert(0, errorCount + " problems\r\n"); + errors.setLength(errors.length() - 2); + throw new AssertionError(errors.toString()); + } + } + + static final class ValidatorStrings { + final String code; + final String javadoc; + ValidatorStrings(String code, String javadoc) { + this.code = code; + this.javadoc = javadoc; + } + } + + static final List VALIDATOR_STRINGS = Arrays.asList( + new ValidatorStrings("Objects.requireNonNull(", "* @throws NullPointerException"), + new ValidatorStrings("ObjectHelper.verifyPositive(", "* @throws IllegalArgumentException") + ); + + static final List EXCEPTION_STRINGS = Arrays.asList( + new ValidatorStrings("throw new NullPointerException(", "* @throws NullPointerException"), + new ValidatorStrings("throw new IllegalArgumentException(", "* @throws IllegalArgumentException"), + new ValidatorStrings("throw new IndexOutOfBoundsException(", "* @throws IndexOutOfBoundsException") + ); + + static final List TYPICAL_ARGUMENT_STRINGS = Arrays.asList( + new ValidatorStrings("source", "* @throws NullPointerException"), + new ValidatorStrings("source1", "* @throws NullPointerException"), + new ValidatorStrings("source2", "* @throws NullPointerException"), + new ValidatorStrings("source3", "* @throws NullPointerException"), + new ValidatorStrings("source4", "* @throws NullPointerException"), + new ValidatorStrings("source5", "* @throws NullPointerException"), + new ValidatorStrings("source6", "* @throws NullPointerException"), + new ValidatorStrings("source7", "* @throws NullPointerException"), + new ValidatorStrings("source8", "* @throws NullPointerException"), + new ValidatorStrings("source9", "* @throws NullPointerException"), + new ValidatorStrings("sources", "* @throws NullPointerException"), + new ValidatorStrings("mapper", "* @throws NullPointerException"), + new ValidatorStrings("combiner", "* @throws NullPointerException"), + new ValidatorStrings("zipper", "* @throws NullPointerException"), + new ValidatorStrings("predicate", "* @throws NullPointerException"), + new ValidatorStrings("item", "* @throws NullPointerException"), + new ValidatorStrings("item1", "* @throws NullPointerException"), + new ValidatorStrings("item2", "* @throws NullPointerException"), + new ValidatorStrings("item3", "* @throws NullPointerException"), + new ValidatorStrings("item4", "* @throws NullPointerException"), + new ValidatorStrings("item5", "* @throws NullPointerException"), + new ValidatorStrings("item6", "* @throws NullPointerException"), + new ValidatorStrings("item7", "* @throws NullPointerException"), + new ValidatorStrings("item8", "* @throws NullPointerException"), + new ValidatorStrings("item9", "* @throws NullPointerException"), + new ValidatorStrings("item10", "* @throws NullPointerException"), + new ValidatorStrings("unit", "* @throws NullPointerException"), + new ValidatorStrings("scheduler", "* @throws NullPointerException"), + new ValidatorStrings("other", "* @throws NullPointerException"), + new ValidatorStrings("fallback", "* @throws NullPointerException"), + new ValidatorStrings("defaultItem", "* @throws NullPointerException"), + new ValidatorStrings("defaultValue", "* @throws NullPointerException"), + new ValidatorStrings("stop", "* @throws NullPointerException"), + new ValidatorStrings("stopPredicate", "* @throws NullPointerException"), + new ValidatorStrings("handler", "* @throws NullPointerException"), + new ValidatorStrings("bufferSupplier", "* @throws NullPointerException"), + new ValidatorStrings("openingIndicator", "* @throws NullPointerException"), + new ValidatorStrings("closingIndicator", "* @throws NullPointerException"), + new ValidatorStrings("boundary", "* @throws NullPointerException"), + new ValidatorStrings("boundaryIndicator", "* @throws NullPointerException"), + new ValidatorStrings("selector", "* @throws NullPointerException"), + new ValidatorStrings("resultSelector", "* @throws NullPointerException"), + new ValidatorStrings("keySelector", "* @throws NullPointerException"), + new ValidatorStrings("valueSelector", "* @throws NullPointerException"), + new ValidatorStrings("valueSupplier", "* @throws NullPointerException"), + new ValidatorStrings("collectionSupplier", "* @throws NullPointerException"), + new ValidatorStrings("onNext", "* @throws NullPointerException"), + new ValidatorStrings("onError", "* @throws NullPointerException"), + new ValidatorStrings("onComplete", "* @throws NullPointerException"), + new ValidatorStrings("onEvent", "* @throws NullPointerException"), + new ValidatorStrings("onAfterNext", "* @throws NullPointerException"), + new ValidatorStrings("onAfterTerminate", "* @throws NullPointerException"), + new ValidatorStrings("onTerminate", "* @throws NullPointerException"), + new ValidatorStrings("onSuccess", "* @throws NullPointerException"), + new ValidatorStrings("onSubscribe", "* @throws NullPointerException"), + new ValidatorStrings("onNotification", "* @throws NullPointerException"), + new ValidatorStrings("onCancel", "* @throws NullPointerException"), + new ValidatorStrings("onDispose", "* @throws NullPointerException"), + new ValidatorStrings("onRequest", "* @throws NullPointerException"), + new ValidatorStrings("onNextMapper", "* @throws NullPointerException"), + new ValidatorStrings("onErrorMapper", "* @throws NullPointerException"), + new ValidatorStrings("onCompleteSupplier", "* @throws NullPointerException"), + new ValidatorStrings("clazz", "* @throws NullPointerException"), + new ValidatorStrings("next", "* @throws NullPointerException"), + new ValidatorStrings("reducer", "* @throws NullPointerException"), + new ValidatorStrings("seed", "* @throws NullPointerException"), + new ValidatorStrings("seedSupplier", "* @throws NullPointerException"), + new ValidatorStrings("mapSupplier", "* @throws NullPointerException"), + new ValidatorStrings("collectionFactory", "* @throws NullPointerException"), + new ValidatorStrings("factory", "* @throws NullPointerException"), + new ValidatorStrings("stage", "* @throws NullPointerException"), + new ValidatorStrings("stream", "* @throws NullPointerException"), + new ValidatorStrings("collector", "* @throws NullPointerException"), + new ValidatorStrings("subscriptionIndicator", "* @throws NullPointerException"), + new ValidatorStrings("itemDelayIndicator", "* @throws NullPointerException"), + new ValidatorStrings("future", "* @throws NullPointerException"), + + new ValidatorStrings("maxConcurrency", "* @throws IllegalArgumentException"), + new ValidatorStrings("parallelism", "* @throws IllegalArgumentException"), + new ValidatorStrings("prefetch", "* @throws IllegalArgumentException"), + new ValidatorStrings("bufferSize", "* @throws IllegalArgumentException"), + new ValidatorStrings("capacityHint", "* @throws IllegalArgumentException"), + new ValidatorStrings("capacity", "* @throws IllegalArgumentException"), + new ValidatorStrings("count", "* @throws IllegalArgumentException"), + new ValidatorStrings("skip", "* @throws IllegalArgumentException"), + new ValidatorStrings("times", "* @throws IllegalArgumentException"), + new ValidatorStrings("n", "* @throws IllegalArgumentException") + ); + +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/ParameterNamesInClassesTest.java b/src/test/java/io/reactivex/rxjava3/validators/ParameterNamesInClassesTest.java new file mode 100644 index 0000000000..2284e32f6a --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/ParameterNamesInClassesTest.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class ParameterNamesInClassesTest { + void method(int paramName) { + // deliberately empty + } + + @Test + public void javacParametersEnabled() throws Exception { + assertEquals("Please enable saving parameter names via the -parameters javac argument", + "paramName", + getClass() + .getDeclaredMethod("method", Integer.TYPE) + .getParameters()[0].getName()); + } +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/PublicFinalMethods.java b/src/test/java/io/reactivex/rxjava3/validators/PublicFinalMethods.java index f7f59d630d..ad7df44964 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/PublicFinalMethods.java +++ b/src/test/java/io/reactivex/rxjava3/validators/PublicFinalMethods.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in diff --git a/src/test/java/io/reactivex/rxjava3/validators/SourceAnnotationCheck.java b/src/test/java/io/reactivex/rxjava3/validators/SourceAnnotationCheck.java new file mode 100644 index 0000000000..dfa4aea548 --- /dev/null +++ b/src/test/java/io/reactivex/rxjava3/validators/SourceAnnotationCheck.java @@ -0,0 +1,501 @@ +/* + * 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. + */ + +package io.reactivex.rxjava3.validators; + +import java.io.File; +import java.nio.file.Files; +import java.util.*; + +import org.junit.Test; + +import io.reactivex.rxjava3.core.*; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.flowables.ConnectableFlowable; +import io.reactivex.rxjava3.observables.ConnectableObservable; +import io.reactivex.rxjava3.parallel.ParallelFlowable; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; +import io.reactivex.rxjava3.processors.*; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.*; +import io.reactivex.rxjava3.testsupport.TestHelper; + +/** + * Parse the given files and check if all public final and public static methods have + * @NonNull or @Nullable annotations specified on their return type and object-type parameters + * as well as @SafeVarargs for varargs. + */ +public class SourceAnnotationCheck { + + @Test + public void checkCompletable() throws Exception { + processFile(Completable.class); + } + + @Test + public void checkSingle() throws Exception { + processFile(Single.class); + } + + @Test + public void checkMaybe() throws Exception { + processFile(Maybe.class); + } + + @Test + public void checkObservable() throws Exception { + processFile(Observable.class); + } + + @Test + public void checkFlowable() throws Exception { + processFile(Flowable.class); + } + + @Test + public void checkParallelFlowable() throws Exception { + processFile(ParallelFlowable.class); + } + + @Test + public void checkConnectableObservable() throws Exception { + processFile(ConnectableObservable.class); + } + + @Test + public void checkConnectableFlowable() throws Exception { + processFile(ConnectableFlowable.class); + } + + @Test + public void checkSubject() throws Exception { + processFile(Subject.class); + } + + @Test + public void checkFlowableProcessor() throws Exception { + processFile(FlowableProcessor.class); + } + + @Test + public void checkDisposable() throws Exception { + processFile(Disposable.class); + } + + @Test + public void checkScheduler() throws Exception { + processFile(Scheduler.class); + } + + @Test + public void checkSchedulers() throws Exception { + processFile(Schedulers.class); + } + + @Test + public void checkAsyncSubject() throws Exception { + processFile(AsyncSubject.class); + } + + @Test + public void checkBehaviorSubject() throws Exception { + processFile(BehaviorSubject.class); + } + + @Test + public void checkPublishSubject() throws Exception { + processFile(PublishSubject.class); + } + + @Test + public void checkReplaySubject() throws Exception { + processFile(ReplaySubject.class); + } + + @Test + public void checkUnicastSubject() throws Exception { + processFile(UnicastSubject.class); + } + + @Test + public void checkSingleSubject() throws Exception { + processFile(SingleSubject.class); + } + + @Test + public void checkMaybeSubject() throws Exception { + processFile(MaybeSubject.class); + } + + @Test + public void checkCompletableSubject() throws Exception { + processFile(CompletableSubject.class); + } + + @Test + public void checkAsyncProcessor() throws Exception { + processFile(AsyncProcessor.class); + } + + @Test + public void checkBehaviorProcessor() throws Exception { + processFile(BehaviorProcessor.class); + } + + @Test + public void checkPublishProcessor() throws Exception { + processFile(PublishProcessor.class); + } + + @Test + public void checkReplayProcessor() throws Exception { + processFile(ReplayProcessor.class); + } + + @Test + public void checkUnicastProcessor() throws Exception { + processFile(UnicastProcessor.class); + } + + @Test + public void checkMulticastProcessor() throws Exception { + processFile(MulticastProcessor.class); + } + + @Test + public void checkRxJavaPlugins() throws Exception { + processFile(RxJavaPlugins.class); + } + + static void processFile(Class clazz) throws Exception { + String baseClassName = clazz.getSimpleName(); + File f = TestHelper.findSource(baseClassName, clazz.getPackage().getName()); + if (f == null) { + return; + } + String fullClassName = clazz.getName(); + + int errorCount = 0; + StringBuilder errors = new StringBuilder(); + + List lines = Files.readAllLines(f.toPath()); + + for (int j = 0; j < lines.size(); j++) { + String line = lines.get(j).trim(); + + if (line.contains("class")) { + continue; + } + if (line.startsWith("public static") + || line.startsWith("public final") + || line.startsWith("protected final") + || line.startsWith("protected abstract") + || line.startsWith("public abstract")) { + int methodArgStart = line.indexOf("("); + + int isBoolean = line.indexOf(" boolean "); + int isInt = line.indexOf(" int "); + int isLong = line.indexOf(" long "); + int isVoid = line.indexOf(" void "); + int isElementType = line.indexOf(" R "); + + boolean hasSafeVarargsAnnotation = false; + + if (!((isBoolean > 0 && isBoolean < methodArgStart) + || (isInt > 0 && isInt < methodArgStart) + || (isLong > 0 && isLong < methodArgStart) + || (isVoid > 0 && isVoid < methodArgStart) + || (isElementType > 0 && isElementType < methodArgStart) + )) { + + boolean annotationFound = false; + for (int k = j - 1; k >= 0; k--) { + + String prevLine = lines.get(k).trim(); + + if (prevLine.startsWith("}") || prevLine.startsWith("*/")) { + break; + } + if (prevLine.startsWith("@NonNull") || prevLine.startsWith("@Nullable")) { + annotationFound = true; + } + if (prevLine.startsWith("@SafeVarargs")) { + hasSafeVarargsAnnotation = true; + } + } + + if (!annotationFound) { + errorCount++; + errors.append("L") + .append(j) + .append(" : Missing return type nullability annotation | ") + .append(line) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + + // Extract arguments + StringBuilder arguments = new StringBuilder(); + int methodArgEnd = line.indexOf(")", methodArgStart); + if (methodArgEnd > 0) { + arguments.append(line.substring(methodArgStart + 1, methodArgEnd)); + } else { + arguments.append(line.substring(methodArgStart + 1)); + for (int k = j + 1; k < lines.size(); k++) { + String ln = lines.get(k).trim(); + int idx = ln.indexOf(")"); + if (idx > 0) { + arguments.append(ln.substring(0, idx)); + break; + } + arguments.append(ln).append(" "); + } + } + + // Strip generics arguments + StringBuilder strippedArguments = new StringBuilder(); + int skippingDepth = 0; + for (int k = 0; k < arguments.length(); k++) { + char c = arguments.charAt(k); + if (c == '<') { + skippingDepth++; + } + else if (c == '>') { + skippingDepth--; + } + else if (skippingDepth == 0) { + strippedArguments.append(c); + } + } + + String strippedArgumentsStr = strippedArguments.toString(); + String[] args = strippedArgumentsStr.split("\\s*,\\s*"); + + for (int k = 0; k < args.length; k++) { + String typeDef = args[k]; + + for (String typeName : CLASS_NAMES) { + String typeNameSpaced = typeName + " "; + + if (typeDef.contains(typeNameSpaced) + && !typeDef.contains("@NonNull") + && !typeDef.contains("@Nullable")) { + + if (!line.contains("@Nullable " + typeName) + && !line.contains("@NonNull " + typeName)) { + errorCount++; + errors.append("L") + .append(j) + .append(" - argument ").append(k + 1) + .append(" : Missing argument type nullability annotation\r\n ") + .append(typeDef).append("\r\n ") + .append(strippedArgumentsStr) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + } + + if (typeDef.contains("final ")) { + errorCount++; + errors.append("L") + .append(j) + .append(" - argument ").append(k + 1) + .append(" : unnecessary final on argument\r\n ") + .append(typeDef).append("\r\n ") + .append(strippedArgumentsStr) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + if (typeDef.contains("@NonNull int") + || typeDef.contains("@NonNull long") + || typeDef.contains("@Nullable int") + || typeDef.contains("@Nullable long") + ) { + errorCount++; + errors.append("L") + .append(j) + .append(" - argument ").append(k + 1) + .append(" : unnecessary nullability annotation\r\n ") + .append(typeDef).append("\r\n ") + .append(strippedArgumentsStr) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + + } + + if (strippedArgumentsStr.contains("...") && !hasSafeVarargsAnnotation) { + errorCount++; + errors.append("L") + .append(j) + .append(" : Missing @SafeVarargs annotation\r\n ") + .append(strippedArgumentsStr) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + + for (String typeName : TYPES_REQUIRING_NONNULL_TYPEARG) { + String pattern = typeName + ".*"; + if (line.contains(pattern) && !line.matches(patternRegex)) { + + errorCount++; + errors.append("L") + .append(j) + .append(" : Missing @NonNull type argument annotation on ") + .append(typeName) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + for (String typeName : TYPES_FORBIDDEN_NONNULL_TYPEARG) { + String patternRegex = ".*" + typeName + "\\<@NonNull (\\? (extends|super) )?" + COMMON_TYPE_ARG_NAMES + "\\>.*"; + + if (line.matches(patternRegex)) { + errorCount++; + errors.append("L") + .append(j) + .append(" : @NonNull type argument should be on the arg declaration ") + .append(typeName) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + + for (String typeName : TYPES_REQUIRING_NONNULL_TYPEARG_ON_FUNC) { + if (line.matches(".*Function[\\d]?\\<.*, (\\? (extends|super) )?" + typeName + ".*")) { + errorCount++; + errors.append("L") + .append(j) + .append(" : Missing @NonNull type argument annotation on Function argument ") + .append(typeName) + .append("\r\n") + .append(" at ") + .append(fullClassName) + .append(".method(") + .append(f.getName()) + .append(":") + .append(j + 1) + .append(")\r\n") + ; + } + } + } + + if (errorCount != 0) { + errors.insert(0, errorCount + " problems\r\n"); + errors.setLength(errors.length() - 2); + throw new AssertionError(errors.toString()); + } + } + + static final List CLASS_NAMES = Arrays.asList( + "TimeUnit", "Scheduler", "Emitter", + + "Completable", "CompletableSource", "CompletableObserver", "CompletableOnSubscribe", + "CompletableTransformer", "CompletableOperator", "CompletableEmitter", "CompletableConverter", + + "Single", "SingleSource", "SingleObserver", "SingleOnSubscribe", + "SingleTransformer", "SingleOperator", "SingleEmitter", "SingleConverter", + + "Maybe", "MaybeSource", "MaybeObserver", "MaybeOnSubscribe", + "MaybeTransformer", "MaybeOperator", "MaybeEmitter", "MaybeConverter", + + "Observable", "ObservableSource", "Observer", "ObservableOnSubscribe", + "ObservableTransformer", "ObservableOperator", "ObservableEmitter", "ObservableConverter", + + "Flowable", "Publisher", "Subscriber", "FlowableSubscriber", "FlowableOnSubscribe", + "FlowableTransformer", "FlowableOperator", "FlowableEmitter", "FlowableConverter", + + "Function", "BiFunction", "Function3", "Function4", "Function5", "Function6", + "Function7", "Function8", "Function9", + + "Action", "Runnable", "Consumer", "BiConsumer", "Supplier", "Callable", "Void", + "Throwable", "Optional", "CompletionStage", "BooleanSupplier", "LongConsumer", + "Predicate", "BiPredicate", "Object", + + "Iterable", "Stream", "Iterator", + + "BackpressureOverflowStrategy", "BackpressureStrategy", + "Subject", "Processor", "FlowableProcessor", + + "T", "R", "U", "V" + ); + + static final List TYPES_REQUIRING_NONNULL_TYPEARG = Arrays.asList( + "Iterable", "Stream", "Publisher", "Processor", "Subscriber", "Optional" + ); + static final List TYPES_FORBIDDEN_NONNULL_TYPEARG = Arrays.asList( + "Iterable", "Stream", "Publisher", "Processor", "Subscriber", "Optional" + ); + + static final List TYPES_REQUIRING_NONNULL_TYPEARG_ON_FUNC = Arrays.asList( + "Iterable", "Stream", "Publisher", "Processor", "Subscriber", "Optional", + "Observer", "SingleObserver", "MaybeObserver", "CompletableObserver" + ); + + static final String COMMON_TYPE_ARG_NAMES = "([A-Z][0-9]?|TOpening|TClosing|TLeft|TLeftEnd|TRight|TRightEnd)"; +} diff --git a/src/test/java/io/reactivex/rxjava3/validators/TestPrefixInMethodName.java b/src/test/java/io/reactivex/rxjava3/validators/TestPrefixInMethodName.java index aedcc603f7..7a75611b10 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/TestPrefixInMethodName.java +++ b/src/test/java/io/reactivex/rxjava3/validators/TestPrefixInMethodName.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ public void checkAndUpdateTestMethodNames() throws Exception { return; } - Queue dirs = new ArrayDeque(); + Queue dirs = new ArrayDeque<>(); StringBuilder fail = new StringBuilder(); fail.append("The following code pattern was found: ").append(pattern).append("\n"); @@ -66,7 +66,7 @@ public void checkAndUpdateTestMethodNames() throws Exception { if (fname.endsWith(".java")) { int lineNum = 0; - List lines = new ArrayList(); + List lines = new ArrayList<>(); BufferedReader in = new BufferedReader(new FileReader(u)); //boolean found = false; try { diff --git a/src/test/java/io/reactivex/rxjava3/validators/TextualAorAn.java b/src/test/java/io/reactivex/rxjava3/validators/TextualAorAn.java index 4f693bd02c..654b4884b4 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/TextualAorAn.java +++ b/src/test/java/io/reactivex/rxjava3/validators/TextualAorAn.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -32,7 +32,7 @@ public void checkFiles() throws Exception { return; } - Queue dirs = new ArrayDeque(); + Queue dirs = new ArrayDeque<>(); File parent = f.getParentFile().getParentFile(); dirs.offer(parent); @@ -53,7 +53,7 @@ public void checkFiles() throws Exception { } else { if (u.getName().endsWith(".java")) { - List lines = new ArrayList(); + List lines = new ArrayList<>(); BufferedReader in = new BufferedReader(new FileReader(u)); try { for (;;) { diff --git a/src/test/java/io/reactivex/rxjava3/validators/TooManyEmptyNewLines.java b/src/test/java/io/reactivex/rxjava3/validators/TooManyEmptyNewLines.java index 22ab721a21..c79c879337 100644 --- a/src/test/java/io/reactivex/rxjava3/validators/TooManyEmptyNewLines.java +++ b/src/test/java/io/reactivex/rxjava3/validators/TooManyEmptyNewLines.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016-present, RxJava Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in @@ -52,7 +52,7 @@ static void findPattern(int newLines) throws Exception { return; } - Queue dirs = new ArrayDeque(); + Queue dirs = new ArrayDeque<>(); StringBuilder fail = new StringBuilder(); fail.append("The following code pattern was found: "); @@ -82,7 +82,7 @@ static void findPattern(int newLines) throws Exception { String fname = u.getName(); if (fname.endsWith(".java")) { - List lines = new ArrayList(); + List lines = new ArrayList<>(); BufferedReader in = new BufferedReader(new FileReader(u)); try { for (;;) { @@ -112,7 +112,14 @@ static void findPattern(int newLines) throws Exception { fail .append(fname) .append("#L").append(i + 1) - .append("\n"); + .append("\n") + .append(" at ") + .append(fname.replace(".java", "")) + .append(".method(") + .append(fname) + .append(":").append(i + 1) + .append(")\n") + ; total++; i += c; } @@ -124,9 +131,7 @@ static void findPattern(int newLines) throws Exception { } } if (total != 0) { - fail.append("Found ") - .append(total) - .append(" instances"); + fail.insert(0, "Found " + total + " instances\n"); System.out.println(fail); throw new AssertionError(fail.toString()); }